docs(decomposition): enhance docstrings and add spec section for selective subtree recomputation
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 1m0s
CI / quality (pull_request) Successful in 1m12s
CI / typecheck (pull_request) Successful in 1m21s
CI / security (pull_request) Successful in 1m30s
CI / push-validation (pull_request) Successful in 30s
CI / build (pull_request) Successful in 41s
CI / helm (pull_request) Successful in 45s
CI / integration_tests (pull_request) Successful in 3m34s
CI / unit_tests (pull_request) Successful in 4m40s
CI / e2e_tests (pull_request) Successful in 4m10s
CI / docker (pull_request) Successful in 1m32s
CI / coverage (pull_request) Successful in 11m7s
CI / benchmark-regression (push) Has been skipped
CI / status-check (pull_request) Successful in 5s
CI / helm (push) Successful in 32s
CI / build (push) Successful in 59s
CI / lint (push) Successful in 1m7s
CI / quality (push) Successful in 1m24s
CI / typecheck (push) Successful in 1m44s
CI / security (push) Successful in 1m46s
CI / push-validation (push) Successful in 24s
CI / integration_tests (push) Successful in 3m36s
CI / unit_tests (push) Successful in 4m59s
CI / e2e_tests (push) Successful in 5m2s
CI / docker (push) Successful in 1m31s
CI / coverage (push) Successful in 10m47s
CI / status-check (push) Successful in 4s
CI / benchmark-regression (pull_request) Successful in 1h4m59s
CI / benchmark-publish (push) Successful in 1h17m4s
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 1m0s
CI / quality (pull_request) Successful in 1m12s
CI / typecheck (pull_request) Successful in 1m21s
CI / security (pull_request) Successful in 1m30s
CI / push-validation (pull_request) Successful in 30s
CI / build (pull_request) Successful in 41s
CI / helm (pull_request) Successful in 45s
CI / integration_tests (pull_request) Successful in 3m34s
CI / unit_tests (pull_request) Successful in 4m40s
CI / e2e_tests (pull_request) Successful in 4m10s
CI / docker (pull_request) Successful in 1m32s
CI / coverage (pull_request) Successful in 11m7s
CI / benchmark-regression (push) Has been skipped
CI / status-check (pull_request) Successful in 5s
CI / helm (push) Successful in 32s
CI / build (push) Successful in 59s
CI / lint (push) Successful in 1m7s
CI / quality (push) Successful in 1m24s
CI / typecheck (push) Successful in 1m44s
CI / security (push) Successful in 1m46s
CI / push-validation (push) Successful in 24s
CI / integration_tests (push) Successful in 3m36s
CI / unit_tests (push) Successful in 4m59s
CI / e2e_tests (push) Successful in 5m2s
CI / docker (push) Successful in 1m31s
CI / coverage (push) Successful in 10m47s
CI / status-check (push) Successful in 4s
CI / benchmark-regression (pull_request) Successful in 1h4m59s
CI / benchmark-publish (push) Successful in 1h17m4s
This commit was merged in pull request #10771.
This commit is contained in:
@@ -18647,6 +18647,38 @@ The way child plan results are merged depends on the resource type:
|
||||
* **Other resources**: Pluggable merge strategies based on resource type
|
||||
* **Non-mergeable resources**: May require sequential execution only
|
||||
|
||||
##### Selective Subtree Recomputation for Decision Correction
|
||||
|
||||
When a decision in the decomposition tree is found to be incorrect, the system
|
||||
supports **selective subtree recomputation**: only the subtree rooted at the
|
||||
target node is re-evaluated, while sibling branches and ancestor nodes are
|
||||
preserved unchanged.
|
||||
|
||||
This is implemented by `DecompositionService.recompute_subtree()`, which:
|
||||
|
||||
1. Accepts a `node_id` identifying the root of the subtree to recompute and
|
||||
an `existing_result` (`DecompositionResult`) containing the current tree.
|
||||
2. Performs a breadth-first search over `children_ids` to collect all nodes
|
||||
in the subtree (the target node and all its descendants).
|
||||
3. Partitions the nodes into two groups:
|
||||
- **Recomputed nodes** — the target node and all its descendants.
|
||||
- **Preserved nodes** — all other nodes (siblings, ancestors, and unrelated
|
||||
branches).
|
||||
4. Returns a `DecisionCorrectionResult` with both groups, the configuration
|
||||
used, and diagnostic metrics (`recomputed_count`, `preserved_count`,
|
||||
`subtree_size`).
|
||||
|
||||
**Acceptance criteria:**
|
||||
|
||||
| # | Criterion | Behaviour |
|
||||
| :- | :-------- | :-------- |
|
||||
| 1 | Leaf recomputation | Only the target leaf is recomputed; all other nodes preserved |
|
||||
| 2 | Middle-node recomputation | Target node and all its descendants recomputed; ancestors and siblings preserved |
|
||||
| 3 | Root recomputation | All nodes recomputed; no nodes preserved |
|
||||
| 4 | Unknown node | `ValueError` raised when `node_id` is not found in the result |
|
||||
| 5 | Custom config | Caller-supplied `DecompositionConfig` is attached to the result |
|
||||
| 6 | Metrics | `recomputed_count`, `preserved_count`, and `subtree_size` always present |
|
||||
|
||||
#### The Plan "Decision Tree" and Visualization
|
||||
|
||||
!!! adr "Architecture Decision"
|
||||
|
||||
@@ -134,7 +134,43 @@ class DecompositionResult:
|
||||
class DecisionCorrectionResult:
|
||||
"""Result of selective subtree recomputation for decision correction.
|
||||
|
||||
Tracks which nodes were recomputed and which were preserved.
|
||||
Captures the outcome of :meth:`DecompositionService.recompute_subtree`,
|
||||
separating the nodes that were re-evaluated from those that were left
|
||||
intact. This enables callers to audit exactly which parts of the
|
||||
decomposition tree changed and which were preserved.
|
||||
|
||||
Attributes:
|
||||
target_node_id: The ``node_id`` of the root node whose subtree was
|
||||
recomputed. All descendants of this node (inclusive) appear in
|
||||
``recomputed_nodes``; all other nodes appear in
|
||||
``preserved_nodes``.
|
||||
recomputed_nodes: Nodes that belong to the recomputed subtree
|
||||
(the target node and all its descendants, identified via BFS
|
||||
over ``children_ids``).
|
||||
preserved_nodes: Nodes outside the recomputed subtree that were
|
||||
left unchanged. Includes sibling branches and all ancestor
|
||||
nodes of the target.
|
||||
config: The :class:`DecompositionConfig` used for this correction
|
||||
pass. Defaults to :class:`DecompositionConfig` with no
|
||||
arguments when the caller does not supply one.
|
||||
metrics: Diagnostic counters produced during recomputation.
|
||||
Always contains the following keys:
|
||||
|
||||
- ``recomputed_count`` -- number of nodes in the recomputed
|
||||
subtree.
|
||||
- ``preserved_count`` -- number of nodes outside the subtree.
|
||||
- ``subtree_size`` -- total nodes visited during the BFS
|
||||
(equal to ``recomputed_count``).
|
||||
|
||||
Usage::
|
||||
|
||||
result = svc.recompute_subtree(
|
||||
node_id="middle_a",
|
||||
existing_result=decomp_result,
|
||||
)
|
||||
print(result.recomputed_node_ids) # ["middle_a", "leaf_a1", ...]
|
||||
print(result.preserved_node_ids) # ["root", "middle_b", ...]
|
||||
print(result.metrics["recomputed_count"])
|
||||
"""
|
||||
|
||||
target_node_id: str
|
||||
|
||||
@@ -399,7 +399,36 @@ class DecompositionService:
|
||||
existing_result: DecompositionResult,
|
||||
config: DecompositionConfig | None = None,
|
||||
) -> DecisionCorrectionResult:
|
||||
"""Recompute only the subtree rooted at node_id."""
|
||||
"""Recompute only the subtree rooted at *node_id*.
|
||||
|
||||
Identifies the subtree via breadth-first search over
|
||||
:attr:`DecompositionNode.children_ids` and partitions the nodes
|
||||
from *existing_result* into two groups: those inside the subtree
|
||||
(recomputed) and those outside it (preserved). Sibling branches
|
||||
and ancestor nodes are always preserved.
|
||||
|
||||
Args:
|
||||
node_id: The ``node_id`` of the root of the subtree to
|
||||
recompute. Must exist in *existing_result*.
|
||||
existing_result: The current :class:`DecompositionResult`
|
||||
whose nodes will be partitioned.
|
||||
config: Optional :class:`DecompositionConfig` to attach to
|
||||
the result. When ``None``, a default
|
||||
:class:`DecompositionConfig` is used.
|
||||
|
||||
Returns:
|
||||
A :class:`DecisionCorrectionResult` containing:
|
||||
|
||||
- ``recomputed_nodes`` -- nodes in the subtree rooted at
|
||||
*node_id* (inclusive).
|
||||
- ``preserved_nodes`` -- all other nodes from
|
||||
*existing_result*.
|
||||
- ``metrics`` -- diagnostic counters (``recomputed_count``,
|
||||
``preserved_count``, ``subtree_size``).
|
||||
|
||||
Raises:
|
||||
ValueError: If *node_id* is not found in *existing_result*.
|
||||
"""
|
||||
cfg = config or DecompositionConfig()
|
||||
node_map: dict[str, DecompositionNode] = {
|
||||
n.node_id: n for n in existing_result.nodes
|
||||
|
||||
Reference in New Issue
Block a user