fix(cli): add --depth-gradient flag to project context set
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 22s
CI / helm (pull_request) Successful in 25s
CI / lint (pull_request) Successful in 3m45s
CI / quality (pull_request) Successful in 4m5s
CI / typecheck (pull_request) Successful in 4m22s
CI / security (pull_request) Successful in 4m31s
CI / integration_tests (pull_request) Successful in 7m27s
CI / unit_tests (pull_request) Successful in 7m55s
CI / docker (pull_request) Successful in 1m30s
CI / coverage (pull_request) Successful in 12m1s
CI / e2e_tests (pull_request) Successful in 22m32s
CI / status-check (pull_request) Successful in 12s
CI / build (push) Successful in 25s
CI / quality (push) Successful in 39s
CI / helm (push) Successful in 41s
CI / lint (push) Successful in 3m36s
CI / typecheck (push) Successful in 3m52s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 4m3s
CI / unit_tests (push) Successful in 4m12s
CI / docker (push) Successful in 1m32s
CI / integration_tests (push) Successful in 7m3s
CI / coverage (push) Successful in 11m52s
CI / e2e_tests (push) Successful in 20m56s
CI / status-check (push) Successful in 1s
CI / benchmark-publish (push) Successful in 28m22s
CI / benchmark-regression (pull_request) Successful in 55m2s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 22s
CI / helm (pull_request) Successful in 25s
CI / lint (pull_request) Successful in 3m45s
CI / quality (pull_request) Successful in 4m5s
CI / typecheck (pull_request) Successful in 4m22s
CI / security (pull_request) Successful in 4m31s
CI / integration_tests (pull_request) Successful in 7m27s
CI / unit_tests (pull_request) Successful in 7m55s
CI / docker (pull_request) Successful in 1m30s
CI / coverage (pull_request) Successful in 12m1s
CI / e2e_tests (pull_request) Successful in 22m32s
CI / status-check (pull_request) Successful in 12s
CI / build (push) Successful in 25s
CI / quality (push) Successful in 39s
CI / helm (push) Successful in 41s
CI / lint (push) Successful in 3m36s
CI / typecheck (push) Successful in 3m52s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 4m3s
CI / unit_tests (push) Successful in 4m12s
CI / docker (push) Successful in 1m32s
CI / integration_tests (push) Successful in 7m3s
CI / coverage (push) Successful in 11m52s
CI / e2e_tests (push) Successful in 20m56s
CI / status-check (push) Successful in 1s
CI / benchmark-publish (push) Successful in 28m22s
CI / benchmark-regression (pull_request) Successful in 55m2s
Add the missing --depth-gradient repeatable flag to `project context set` per specification. The flag accepts HOP:INT_OR_NAME format to control context detail depth degradation with graph distance. Parsing validates hop as integer and value as integer or recognized detail level name. ISSUES CLOSED: #889
This commit was merged in pull request #1186.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
Feature: Project context depth-gradient flag (B2.cli)
|
||||
As a CleverAgents user
|
||||
I want to set per-hop depth gradients via ``project context set --depth-gradient``
|
||||
So that context detail depth degrades with graph distance from focus nodes
|
||||
|
||||
Background:
|
||||
Given a project context CLI in-memory database is initialized
|
||||
And a project "local/ctx-app" exists for context CLI
|
||||
|
||||
# ── single depth-gradient value ─────────────────────────────
|
||||
|
||||
Scenario: Set a single depth-gradient with integer value
|
||||
When I run context set on "local/ctx-app" with depth-gradient "0:9"
|
||||
Then the context set command should succeed
|
||||
And the stored ACMS depth gradient should have hop "0" with value 9
|
||||
|
||||
Scenario: Set a single depth-gradient with named level value
|
||||
When I run context set on "local/ctx-app" with depth-gradient "1:SIGNATURES"
|
||||
Then the context set command should succeed
|
||||
And the stored ACMS depth gradient should have hop "1" with string value "SIGNATURES"
|
||||
|
||||
# ── multiple depth-gradient values ──────────────────────────
|
||||
|
||||
Scenario: Set multiple depth-gradient values
|
||||
When I run context set on "local/ctx-app" with depth-gradients "0:9,1:4,2:MODULE_LISTING"
|
||||
Then the context set command should succeed
|
||||
And the stored ACMS depth gradient should have hop "0" with value 9
|
||||
And the stored ACMS depth gradient should have hop "1" with value 4
|
||||
And the stored ACMS depth gradient should have hop "2" with string value "MODULE_LISTING"
|
||||
|
||||
# ── invalid format ──────────────────────────────────────────
|
||||
|
||||
Scenario: Invalid depth-gradient missing colon fails
|
||||
When I run context set on "local/ctx-app" with depth-gradient "bad"
|
||||
Then the context set command should fail
|
||||
|
||||
Scenario: Invalid depth-gradient non-integer hop fails
|
||||
When I run context set on "local/ctx-app" with depth-gradient "abc:5"
|
||||
Then the context set command should fail
|
||||
|
||||
# ── display in show command ─────────────────────────────────
|
||||
|
||||
Scenario: Depth gradient is displayed in context show output
|
||||
Given I have set depth-gradient "0:9,1:SIGNATURES" on "local/ctx-app"
|
||||
When I run context show on "local/ctx-app" without options
|
||||
Then the context show command should succeed
|
||||
And the context show output should contain "Depth gradient"
|
||||
And the context show output should contain "0:9"
|
||||
And the context show output should contain "1:SIGNATURES"
|
||||
@@ -0,0 +1,132 @@
|
||||
"""Step definitions for ``project_context_depth_gradient.feature``.
|
||||
|
||||
Tests the ``--depth-gradient`` flag on ``agents project context set``
|
||||
and its display in ``agents project context show``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from behave import given, then, when # type: ignore[import-untyped]
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Helpers — reuse the shared infrastructure from
|
||||
# project_context_cli_steps (background steps, _run_with_container,
|
||||
# _mock_container, etc. are already registered).
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
def _run_with_container_local(
|
||||
context: Any, func: Any, *args: Any, **kwargs: Any
|
||||
) -> None:
|
||||
"""Delegate to the shared test runner from project_context_cli_steps."""
|
||||
from features.steps.project_context_cli_steps import (
|
||||
_run_with_container,
|
||||
)
|
||||
|
||||
_run_with_container(context, func, *args, **kwargs)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# When steps
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
@when(
|
||||
'I run context set on "{project}" with depth-gradient "{gradient}"',
|
||||
)
|
||||
def step_ctx_set_depth_gradient_single(
|
||||
context: Any, project: str, gradient: str
|
||||
) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container_local(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="default",
|
||||
depth_gradient=[gradient],
|
||||
)
|
||||
|
||||
|
||||
@when(
|
||||
'I run context set on "{project}" with depth-gradients "{gradients}"',
|
||||
)
|
||||
def step_ctx_set_depth_gradient_multi(
|
||||
context: Any, project: str, gradients: str
|
||||
) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
gradient_list = [g.strip() for g in gradients.split(",")]
|
||||
_run_with_container_local(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="default",
|
||||
depth_gradient=gradient_list,
|
||||
)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Given steps
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
@given('I have set depth-gradient "{gradients}" on "{project}"')
|
||||
def step_given_depth_gradient(context: Any, gradients: str, project: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
gradient_list = [g.strip() for g in gradients.split(",")]
|
||||
_run_with_container_local(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="default",
|
||||
depth_gradient=gradient_list,
|
||||
)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Then steps
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
def _read_stored_acms(context: Any) -> dict[str, Any]:
|
||||
"""Read the stored ACMS config from the test DB."""
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
_read_acms_config,
|
||||
)
|
||||
|
||||
return _read_acms_config(context.ctx_session_factory, "local/ctx-app")
|
||||
|
||||
|
||||
@then('the stored ACMS depth gradient should have hop "{hop}" with value {value:d}')
|
||||
def step_depth_gradient_int(context: Any, hop: str, value: int) -> None:
|
||||
acms = _read_stored_acms(context)
|
||||
dg = acms.get("depth_gradient", {})
|
||||
assert hop in dg, f"Hop '{hop}' not in depth_gradient: {dg}"
|
||||
assert dg[hop] == value, f"Expected {value} for hop {hop}, got {dg[hop]}"
|
||||
|
||||
|
||||
@then(
|
||||
'the stored ACMS depth gradient should have hop "{hop}" with string value "{value}"'
|
||||
)
|
||||
def step_depth_gradient_str(context: Any, hop: str, value: str) -> None:
|
||||
acms = _read_stored_acms(context)
|
||||
dg = acms.get("depth_gradient", {})
|
||||
assert hop in dg, f"Hop '{hop}' not in depth_gradient: {dg}"
|
||||
assert dg[hop] == value, f"Expected '{value}' for hop {hop}, got {dg[hop]}"
|
||||
|
||||
|
||||
@then('the context show output should contain "{text}"')
|
||||
def step_show_output_contains(context: Any, text: str) -> None:
|
||||
assert text in context.ctx_output, (
|
||||
f"Expected '{text}' in output, got: {context.ctx_output!r}"
|
||||
)
|
||||
@@ -543,6 +543,16 @@ def context_set(
|
||||
help="Temporal scope: current, recent, or all",
|
||||
),
|
||||
] = None,
|
||||
depth_gradient: Annotated[
|
||||
list[str] | None,
|
||||
typer.Option(
|
||||
"--depth-gradient",
|
||||
help=(
|
||||
"Per-hop detail depth override (repeatable). "
|
||||
"Format: HOP:INT_OR_NAME, e.g. 0:9, 1:SIGNATURES"
|
||||
),
|
||||
),
|
||||
] = None,
|
||||
auto_refresh: Annotated[
|
||||
bool | None,
|
||||
typer.Option(
|
||||
@@ -642,6 +652,37 @@ def context_set(
|
||||
acms["temporal_scope"] = temporal_scope
|
||||
if auto_refresh is not None:
|
||||
acms["auto_refresh"] = auto_refresh
|
||||
if depth_gradient is not None:
|
||||
parsed_gradient: dict[str, int | str] = {}
|
||||
for entry in depth_gradient:
|
||||
if ":" not in entry:
|
||||
err_console.print(
|
||||
f"[red]Invalid depth-gradient '{entry}': "
|
||||
f"expected HOP:INT_OR_NAME format[/red]"
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
hop_str, _, value_str = entry.partition(":")
|
||||
try:
|
||||
hop = int(hop_str)
|
||||
except ValueError:
|
||||
err_console.print(
|
||||
f"[red]Invalid depth-gradient hop '{hop_str}': "
|
||||
f"hop must be a non-negative integer[/red]"
|
||||
)
|
||||
raise typer.Exit(1) from None
|
||||
if hop < 0:
|
||||
err_console.print(
|
||||
f"[red]Invalid depth-gradient hop '{hop}': "
|
||||
f"hop must be a non-negative integer[/red]"
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
# Value: try int first, else keep as named level string
|
||||
try:
|
||||
parsed_value: int | str = int(value_str)
|
||||
except ValueError:
|
||||
parsed_value = value_str
|
||||
parsed_gradient[str(hop)] = parsed_value
|
||||
acms["depth_gradient"] = parsed_gradient
|
||||
|
||||
_write_policy(session_factory, project, policy, acms)
|
||||
|
||||
@@ -852,6 +893,14 @@ def context_show(
|
||||
f" Default breadth: {acms.get('default_breadth', _DEFAULT_BREADTH)}"
|
||||
)
|
||||
lines.append(f" Default depth: {acms.get('default_depth', _DEFAULT_DEPTH)}")
|
||||
dg = acms.get("depth_gradient", {})
|
||||
if dg:
|
||||
gradient_parts = [
|
||||
f"{h}:{v}" for h, v in sorted(dg.items(), key=lambda x: int(x[0]))
|
||||
]
|
||||
lines.append(f" Depth gradient: {', '.join(gradient_parts)}")
|
||||
else:
|
||||
lines.append(" Depth gradient: (none)")
|
||||
lines.append(
|
||||
f" Skeleton ratio: {acms.get('skeleton_ratio', _DEFAULT_SKELETON_RATIO)}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user