fix(plan): use structured alternatives objects in plan explain output per spec
CI / benchmark-publish (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 38s
CI / helm (pull_request) Successful in 45s
CI / build (pull_request) Successful in 1m0s
CI / lint (pull_request) Failing after 1m9s
CI / quality (pull_request) Successful in 1m29s
CI / typecheck (pull_request) Successful in 1m37s
CI / security (pull_request) Successful in 1m48s
CI / e2e_tests (pull_request) Successful in 3m45s
CI / integration_tests (pull_request) Failing after 4m34s
CI / unit_tests (pull_request) Failing after 4m56s
CI / coverage (pull_request) Has been skipped
CI / docker (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Failing after 1m14s
CI / status-check (pull_request) Failing after 15s
CI / benchmark-publish (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 38s
CI / helm (pull_request) Successful in 45s
CI / build (pull_request) Successful in 1m0s
CI / lint (pull_request) Failing after 1m9s
CI / quality (pull_request) Successful in 1m29s
CI / typecheck (pull_request) Successful in 1m37s
CI / security (pull_request) Successful in 1m48s
CI / e2e_tests (pull_request) Successful in 3m45s
CI / integration_tests (pull_request) Failing after 4m34s
CI / unit_tests (pull_request) Failing after 4m56s
CI / coverage (pull_request) Has been skipped
CI / docker (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Failing after 1m14s
CI / status-check (pull_request) Failing after 15s
Convert alternatives_considered list of strings to structured objects with index (1-based), description, and chosen fields in _build_explain_dict(). Rename output field from alternatives_considered to alternatives. Update BDD tests in plan_explain.feature, plan_explain_cli_coverage.feature, and plan_explain_steps.py to validate the new structured format. Closes #9166
This commit is contained in:
@@ -13,7 +13,7 @@ Feature: Plan explain and decision tree CLI commands
|
||||
And the explain dict should contain key "question"
|
||||
And the explain dict should contain key "chosen"
|
||||
And the explain dict should contain key "type"
|
||||
And the explain dict should contain key "alternatives_considered"
|
||||
And the explain dict should contain key "alternatives"
|
||||
And the explain dict should not contain key "rationale"
|
||||
And the explain dict should not contain key "context_snapshot"
|
||||
|
||||
@@ -45,8 +45,10 @@ Feature: Plan explain and decision tree CLI commands
|
||||
Scenario: Explain includes alternatives by default
|
||||
Given a test decision with alternatives for explain
|
||||
When I build the explain dict with default options
|
||||
Then the explain dict should contain key "alternatives_considered"
|
||||
Then the explain dict should contain key "alternatives"
|
||||
And the alternatives list should have 2 items
|
||||
And each alternative should have keys "index", "description", and "chosen"
|
||||
And exactly one alternative should have chosen set to true
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# plan explain - json format
|
||||
|
||||
@@ -49,7 +49,7 @@ Feature: Plan explain and tree CLI command coverage
|
||||
Given pec a mock DecisionService returning a decision with alternatives
|
||||
When pec I invoke "explain" with the decision id
|
||||
Then pec the exit code should be 0
|
||||
And pec the output should contain "alternatives_considered"
|
||||
And pec the output should contain "alternatives"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# tree_decisions_cmd - rich tree format
|
||||
|
||||
@@ -322,10 +322,27 @@ def step_snapshot_has_key(context: Context, key: str) -> None:
|
||||
|
||||
@then("the alternatives list should have {count:d} items")
|
||||
def step_alternatives_count(context: Context, count: int) -> None:
|
||||
alts = context.pe_explain_dict["alternatives_considered"]
|
||||
alts = context.pe_explain_dict["alternatives"]
|
||||
assert len(alts) == count, f"Expected {count} alternatives, got {len(alts)}"
|
||||
|
||||
|
||||
@then('each alternative should have keys "index", "description", and "chosen"')
|
||||
def step_alternatives_have_required_keys(context: Context) -> None:
|
||||
alts = context.pe_explain_dict["alternatives"]
|
||||
for i, alt in enumerate(alts):
|
||||
assert isinstance(alt, dict), f"Alternative {i} is not a dict: {alt!r}"
|
||||
assert "index" in alt, f"Alternative {i} missing 'index' key: {list(alt.keys())}"
|
||||
assert "description" in alt, f"Alternative {i} missing 'description' key: {list(alt.keys())}"
|
||||
assert "chosen" in alt, f"Alternative {i} missing 'chosen' key: {list(alt.keys())}"
|
||||
|
||||
|
||||
@then("exactly one alternative should have chosen set to true")
|
||||
def step_exactly_one_chosen(context: Context) -> None:
|
||||
alts = context.pe_explain_dict["alternatives"]
|
||||
chosen_count = sum(1 for alt in alts if alt.get("chosen") is True)
|
||||
assert chosen_count == 1, f"Expected exactly 1 chosen alternative, got {chosen_count}"
|
||||
|
||||
|
||||
@then('the json output should contain "{text}"')
|
||||
def step_json_contains(context: Context, text: str) -> None:
|
||||
assert text in context.pe_json_output, f"Expected '{text}' in JSON output"
|
||||
|
||||
@@ -3909,7 +3909,14 @@ def _build_explain_dict(
|
||||
"is_correction": decision.is_correction,
|
||||
"superseded": decision.is_superseded,
|
||||
"created_at": decision.created_at.isoformat(),
|
||||
"alternatives_considered": decision.alternatives_considered,
|
||||
"alternatives": [
|
||||
{
|
||||
"index": i + 1,
|
||||
"description": alt,
|
||||
"chosen": alt == decision.chosen_option,
|
||||
}
|
||||
for i, alt in enumerate(decision.alternatives_considered)
|
||||
],
|
||||
}
|
||||
if show_reasoning:
|
||||
data["rationale"] = decision.rationale
|
||||
|
||||
Reference in New Issue
Block a user