You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: plans/active/PLAN-TRACE-TOOL-V2.md
+19-8Lines changed: 19 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -26,9 +26,9 @@ Depends on: v1 trace tool (shipped via PR-TRACE-1a through PR-TRACE-4, all merge
26
26
27
27
## PR breakdown — overview
28
28
29
-
| PR | Scope | Ontology bump | Areas of concern | Test buckets | Independent of |
30
-
| --- | --- | --- | --- | --- | --- |
31
-
| PR-TRACE-V2 | Tree output, configurable collapse, source-relative ranking, bidirectional traversal, `min_result_nodes` retry | none | BFS→tree conversion correctness (children nesting, collapsed reparenting); bidirectional shared visited set; source-relative priority table drift from VALID_ROLES; hint function migration from flat edges to tree walk; breaking API surface in server.py |`tests/test_mcp_trace.py` (all 40 v1 tests updated + 21 new tests) | —|
29
+
| PR | Scope | Ontology bump | Areas of concern | Test buckets |
30
+
| --- | --- | --- | --- | --- |
31
+
| PR-TRACE-V2 | Tree output, configurable collapse, source-relative ranking, bidirectional traversal, `min_result_nodes` retry | none | BFS→tree conversion correctness (children nesting, collapsed reparenting); bidirectional shared visited set; source-relative priority table drift from VALID_ROLES; hint function migration from flat edges to tree walk; breaking API surface in server.py |`tests/test_mcp_trace.py` (37 v1 tests updated + 3 removed + 22 new = 59 total)|
32
32
33
33
Single PR. No sub-PR dependencies.
34
34
@@ -40,6 +40,7 @@ Single PR. No sub-PR dependencies.
40
40
|`min_result_nodes` retry budget | One retry with doubled `fan_out_cap`. No configurable retry count. |
41
41
| Bidirectional `edge_types` per direction | Same `edge_types` for both directions. Issue two unidirectional calls if per-direction types are needed. |
42
42
|`collapse_roles` interaction with `prune_roles`| Independent. A role can be in one, both, or neither. Default: `collapse_roles=["OTHER"]`, `prune_roles` unset. |
43
+
|`collapse_trivial=False` vs `collapse_roles`|`collapse_trivial` is the master toggle. When `False`, collapsing is disabled regardless of `collapse_roles`. `collapse_roles` only takes effect when `collapse_trivial=True`. |
43
44
| Collapsed intermediate accessibility | Retained in `nodes` dict (unlike v1 which removed them). Not nested in tree but accessible standalone. |
44
45
| Bidirectional shared visited set | Yes — nodes discovered in "out" are not re-visited in "in" (and vice versa). |
45
46
| Bidirectional duplicate node handling | Node appears once in `nodes`. In tree, appears under direction discovered first; second direction produces a leaf TreeNode with its `edge_from_parent.direction`. |
@@ -59,6 +60,7 @@ Single PR. No sub-PR dependencies.
- Add `_SOURCE_RELATIVE_PRIORITY: dict[str, dict[str, int]]` table keyed by source node role → target role priority. All role strings must be members of `VALID_ROLES` from `java_ontology.py` (startup assertion validates this).
@@ -67,6 +69,7 @@ Single PR. No sub-PR dependencies.
-`collapse_trivial` is the master toggle: when `False`, no collapsing happens regardless of `collapse_roles` value. When `True`, `collapse_roles` and `collapse_min_chain_length` control the heuristic. If `collapse_trivial=False` and `collapse_roles` is set, `collapse_roles` is ignored.
70
73
- Refactor `_collapse_trivial_chains` to accept `collapse_roles` set and `collapse_min_chain_length`. Role check uses configurable set instead of hardcoded `("OTHER", None)`.
@@ -78,14 +81,22 @@ Single PR. No sub-PR dependencies.
78
81
-`stats` aggregates both traversals.
79
82
-`ranked_leaves` merges and re-ranks from both directions.
80
83
- Duplicate nodes (discovered in both directions) appear under the direction that discovered them first; second direction produces a leaf TreeNode.
84
+
-**Shared visited set advisory**: when `direction="both"` and the shared visited set actually suppresses exploration in the second direction (i.e., some nodes discovered in pass 1 were eligible for discovery in pass 2), emit an advisory: `"bidirectional trace: N nodes explored in the first direction were not re-visited in the second direction"`. This gives agents a completeness signal.
81
85
82
86
**`min_result_nodes` retry:**
83
87
- Add parameter `min_result_nodes: int = 0`.
84
88
- When `min_result_nodes > 0` and initial BFS produces fewer result nodes than target, re-run with `fan_out_cap * 2` (one retry, still clamped by `max_nodes_discovered`). If still below target, return what it has with an advisory.
85
89
86
90
**BFS-to-tree conversion:**
87
-
- Add `_build_tree` helper that converts the BFS edge list into nested `TreeNode` structure, handling collapsed intermediates and cross-service boundary metadata.
88
-
- Add `_build_ranked_leaves` helper that replaces `_enumerate_paths`, producing `RankedLeaf` objects from the tree leaves.
91
+
- Add `_build_tree` helper that converts the internal flat edge list into nested `TreeNode` structure. Contract:
-**Multi-seed roots**: top-level `tree` list contains one `TreeNode` per seed ID. Each seed node has `edge_from_parent=None` and `children` populated from its outgoing/incoming edges.
94
+
-**Flat→nested conversion**: build an adjacency map `from_id → [edges]` from the flat list. For each seed, recursively descend: for every edge from the current node, create a child `TreeNode` with the target's ID, populate `edge_from_parent` from the edge metadata, and recurse into the target's children.
95
+
-**Collapsed intermediate reparenting**: collapsed edges already represent A→C (the collapse pass rewrote them). The collapsed intermediate's ID is in `collapsed_intermediates` on the merged edge. `_build_tree` creates the `TreeNode` for C as a direct child of A with `collapsed=True` and `collapsed_intermediates` carried over. The intermediate B is not in the tree (retained in `nodes` dict for standalone access).
96
+
-**Cross-service boundary metadata**: carried from the flat edge's `cross_service_boundary` flag into the child's `edge_from_parent.cross_service_boundary`. Boundary nodes that are also frontier-stopped have `children=[]`.
97
+
-**Cycle safety**: the adjacency map is a DAG by construction (BFS visited set prevents cycles). No additional cycle detection needed.
98
+
-**Output**: `list[TreeNode]` — one per seed, with fully nested children.
99
+
- Add `_build_ranked_leaves` helper that replaces `_enumerate_paths`, producing `RankedLeaf` objects from the tree leaves. Walk the tree to find all leaf nodes (children=[]), compute `depth` (number of edges from root), `leaf_role` from `nodes[leaf_id].role`, and `score` from role priority + confidence. Sort descending by score; cap at `max_paths`.
89
100
90
101
**Internal refactoring:**
91
102
- BFS loop still builds an internal flat edge representation during traversal, then converts to tree post-BFS (after collapse pass).
@@ -113,7 +124,7 @@ Single PR. No sub-PR dependencies.
113
124
114
125
### 4. `tests/test_mcp_trace.py`
115
126
116
-
- Update all 40 existing v1 tests to use new output format:
127
+
- Update 37 existing v1 tests to use new output format (3 tests that test removed concepts are replaced — see below):
117
128
-`result.edges[...]` → walk `result.tree` children.
118
129
-`result.paths` → `result.ranked_leaves`.
119
130
-`e.collapsed` → `tree_node.collapsed`.
@@ -227,11 +238,11 @@ Single PR. No sub-PR dependencies.
227
238
228
239
| # | Step | File(s) | Done when |
229
240
| - | - | - | - |
230
-
| 1 | Define `TreeNode`, `EdgeFromParent`, `RankedLeaf` models; update `TraceOutput`|`mcp_trace.py`| Models validate with pydantic; `TraceOutput` has `tree` and `ranked_leaves` fields |
241
+
| 1 | Define `TreeNode`, `EdgeFromParent`, `RankedLeaf` models; update `TraceOutput`; update `__all__` (remove `TraceEdge`/`TracePath`, add new models) |`mcp_trace.py`| Models validate with pydantic; `TraceOutput` has `tree` and `ranked_leaves` fields; `__all__` lists only live exports|
| 3 | Refactor `_fan_out_sort_key` to accept `source_role` and use source-relative priority |`mcp_trace.py`| Unit-callable with source role; falls back to static when unmapped |
233
244
| 4 | Refactor `_collapse_trivial_chains` to accept `collapse_roles` and `collapse_min_chain_length`; retain intermediates in `nodes`|`mcp_trace.py`| Configurable roles + chain length; intermediates accessible in `nodes`|
0 commit comments