docs(specification): add deleted_at field to agents project delete JSON/YAML output #8192
@@ -800,6 +800,13 @@ uko-oo:Class` triple emission in `PythonAnalyzer._extract_class()` so that
|
||||
had `@tdd_expected_fail` removed and now run as permanent regression guards.
|
||||
Net result: 629 features active in CI (up from ~545), zero `@skip` tags remain.
|
||||
|
||||
- **Spec alignment for `agents project delete` output** (#7872): Updated
|
||||
`docs/specification.md` to document the correct JSON/YAML output structure
|
||||
for the project delete command, replacing the legacy `deletion_summary`
|
||||
object with the `deleted`, `success`, and `deleted_at` fields that match
|
||||
the actual implementation introduced in PR #6639. Added BDD feature file
|
||||
and step definitions to validate the output format.
|
||||
|
||||
- **Git Worktree Sandbox Apply** (#4454): The `plan apply` command now merges
|
||||
LLM-generated changes via `git merge` from an isolated worktree branch
|
||||
instead of flat `shutil.copy2`. Displays spec-aligned Apply Summary
|
||||
|
||||
@@ -3605,21 +3605,9 @@ Delete a project and unlink all associated resources. The resources themselves r
|
||||
"status": "ok",
|
||||
"exit_code": 0,
|
||||
"data": {
|
||||
"deletion_summary": {
|
||||
"project": "local/docs",
|
||||
"resources_unlinked": 1,
|
||||
"data_dir": "/repos/docs/.cleveragents"
|
||||
},
|
||||
"index_cleanup": {
|
||||
"text_index": "cleared",
|
||||
"vectors": "240 removed",
|
||||
"graph_triples": "none",
|
||||
"storage_freed_mb": 12
|
||||
},
|
||||
"backups": {
|
||||
"snapshot": "/backups/local-docs-2026-02-08.tgz",
|
||||
"retention_days": 7
|
||||
}
|
||||
"deleted": "local/docs",
|
||||
"success": true,
|
||||
"deleted_at": "2026-04-10T19:45:43.123456+00:00"
|
||||
},
|
||||
"timing": { "duration_ms": 530 },
|
||||
"messages": [{ "level": "ok", "text": "Project deleted" }]
|
||||
@@ -3633,18 +3621,9 @@ Delete a project and unlink all associated resources. The resources themselves r
|
||||
status: ok
|
||||
exit_code: 0
|
||||
data:
|
||||
deletion_summary:
|
||||
project: local/docs
|
||||
resources_unlinked: 1
|
||||
data_dir: /repos/docs/.cleveragents
|
||||
index_cleanup:
|
||||
text_index: cleared
|
||||
vectors: 240 removed
|
||||
graph_triples: none
|
||||
storage_freed_mb: 12
|
||||
backups:
|
||||
snapshot: /backups/local-docs-2026-02-08.tgz
|
||||
retention_days: 7
|
||||
deleted: local/docs
|
||||
success: true
|
||||
deleted_at: '2026-04-10T19:45:43.123456+00:00'
|
||||
timing:
|
||||
duration_ms: 530
|
||||
messages:
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
Feature: Project CLI delete spec alignment
|
||||
As a developer
|
||||
I want the project delete command output to match the v3 specification
|
||||
So that JSON and YAML output include the deleted, success, and deleted_at fields
|
||||
|
||||
Background:
|
||||
Given a project delete spec alignment CLI runner
|
||||
And a project delete spec alignment mocked repository
|
||||
|
||||
Scenario: Project delete JSON output includes deleted field
|
||||
Given a project delete spec alignment project "local/docs" exists
|
||||
When I run project delete spec alignment for "local/docs" with format "json"
|
||||
Then the project delete spec alignment output should contain "deleted"
|
||||
|
||||
Scenario: Project delete JSON output includes success field
|
||||
Given a project delete spec alignment project "local/docs" exists
|
||||
When I run project delete spec alignment for "local/docs" with format "json"
|
||||
Then the project delete spec alignment output should contain "success"
|
||||
|
||||
Scenario: Project delete JSON output includes deleted_at field
|
||||
Given a project delete spec alignment project "local/docs" exists
|
||||
When I run project delete spec alignment for "local/docs" with format "json"
|
||||
Then the project delete spec alignment output should contain "deleted_at"
|
||||
|
||||
Scenario: Project delete JSON output does not include deletion_summary
|
||||
Given a project delete spec alignment project "local/docs" exists
|
||||
When I run project delete spec alignment for "local/docs" with format "json"
|
||||
Then the project delete spec alignment output should not contain "deletion_summary"
|
||||
|
||||
Scenario: Project delete YAML output includes deleted field
|
||||
Given a project delete spec alignment project "local/docs" exists
|
||||
When I run project delete spec alignment for "local/docs" with format "yaml"
|
||||
Then the project delete spec alignment output should contain "deleted"
|
||||
|
||||
Scenario: Project delete YAML output includes success field
|
||||
Given a project delete spec alignment project "local/docs" exists
|
||||
When I run project delete spec alignment for "local/docs" with format "yaml"
|
||||
Then the project delete spec alignment output should contain "success"
|
||||
|
||||
Scenario: Project delete YAML output includes deleted_at field
|
||||
Given a project delete spec alignment project "local/docs" exists
|
||||
When I run project delete spec alignment for "local/docs" with format "yaml"
|
||||
Then the project delete spec alignment output should contain "deleted_at"
|
||||
|
||||
Scenario: Project delete YAML output does not include deletion_summary
|
||||
Given a project delete spec alignment project "local/docs" exists
|
||||
When I run project delete spec alignment for "local/docs" with format "yaml"
|
||||
Then the project delete spec alignment output should not contain "deletion_summary"
|
||||
|
||||
Scenario: Project delete success field is true
|
||||
Given a project delete spec alignment project "local/docs" exists
|
||||
When I run project delete spec alignment for "local/docs" with format "json"
|
||||
Then the project delete spec alignment JSON success field should be true
|
||||
|
||||
Scenario: Project delete deleted field matches project name
|
||||
Given a project delete spec alignment project "local/docs" exists
|
||||
When I run project delete spec alignment for "local/docs" with format "json"
|
||||
Then the project delete spec alignment JSON deleted field should be "local/docs"
|
||||
@@ -0,0 +1,131 @@
|
||||
"""Step definitions for project_cli_delete_spec_alignment.feature.
|
||||
|
||||
Validates that the ``agents project delete`` command produces JSON/YAML output
|
||||
with the ``deleted``, ``success``, and ``deleted_at`` fields as specified in
|
||||
docs/specification.md (§agents project delete), matching the implementation
|
||||
introduced in PR #6639.
|
||||
|
||||
These tests exercise the ``format_output`` function directly with the data
|
||||
structure that the ``delete`` command produces, ensuring the spec-aligned
|
||||
output fields are present and correctly structured.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
from behave import given, then, when # type: ignore[import-untyped]
|
||||
|
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Background
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@given("a project delete spec alignment CLI runner")
|
||||
def step_pdsa_runner(context: Any) -> None:
|
||||
"""Initialise context storage for the spec alignment tests."""
|
||||
context.pdsa_output = ""
|
||||
context.pdsa_project_name = ""
|
||||
|
||||
|
||||
@given("a project delete spec alignment mocked repository")
|
||||
def step_pdsa_mock_repo(context: Any) -> None:
|
||||
"""No-op: these tests call format_output directly, no repo needed."""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Given
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@given('a project delete spec alignment project "{name}" exists')
|
||||
def step_pdsa_project_exists(context: Any, name: str) -> None:
|
||||
"""Record the project name that will be used in the delete output."""
|
||||
context.pdsa_project_name = name
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# When
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@when('I run project delete spec alignment for "{name}" with format "{fmt}"')
|
||||
def step_pdsa_run_delete(context: Any, name: str, fmt: str) -> None:
|
||||
"""Simulate the project delete command output by calling format_output directly.
|
||||
|
||||
The ``delete`` command in ``cleveragents.cli.commands.project`` calls::
|
||||
|
||||
format_output(
|
||||
{"deleted": name, "success": True, "deleted_at": datetime.now(tz=UTC)},
|
||||
output_format,
|
||||
)
|
||||
|
||||
We replicate that call here and capture the output written to sys.stdout.
|
||||
"""
|
||||
import sys
|
||||
|
HAL9001
commented
Suggestion (non-blocking): Automated by CleverAgents Bot Suggestion (non-blocking): `import sys`, `from io import StringIO`, and `from cleveragents.cli.formatting import format_output` are imported inside this function body rather than at the top of the file. Project rules require all imports at the top of the file. While similar deferred-import patterns exist in other step files (an established codebase convention), the canonical pattern in this project for step-file imports is a top-level `try/except ImportError: pass` block. Consider moving these to the module top-level to keep the file consistent with newer step files.
---
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker
HAL9001
commented
BLOCKER — Inline imports violate the project import rule. Three imports ( Fix — move these to the top of the file alongside the existing imports: Then remove the three import lines from inside the function body. This is also likely contributing to the Automated by CleverAgents Bot **BLOCKER — Inline imports violate the project import rule.**
Three imports (`sys`, `StringIO`, `format_output`) are declared inside the function body rather than at the top of the file. CONTRIBUTING.md requires all Python imports to be at the top of the file. The only permitted exception is `if TYPE_CHECKING:` blocks.
Fix — move these to the top of the file alongside the existing imports:
```python
import sys
from io import StringIO
from cleveragents.cli.formatting import format_output
```
Then remove the three import lines from inside the function body. This is also likely contributing to the `unit_tests` CI failure if Pyright rejects the function-scoped imports.
---
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker
|
||||
from io import StringIO
|
||||
|
||||
from cleveragents.cli.formatting import format_output
|
||||
|
||||
deleted_at = datetime(2026, 4, 10, 19, 45, 43, 123456, tzinfo=UTC)
|
||||
data: dict[str, object] = {
|
||||
"deleted": name,
|
||||
"success": True,
|
||||
"deleted_at": deleted_at,
|
||||
}
|
||||
|
||||
buf = StringIO()
|
||||
real_stdout = sys.stdout
|
||||
sys.stdout = buf
|
||||
try:
|
||||
format_output(data, fmt)
|
||||
finally:
|
||||
sys.stdout = real_stdout
|
||||
|
||||
context.pdsa_output = buf.getvalue()
|
||||
context.pdsa_format = fmt
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Then
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@then('the project delete spec alignment output should contain "{text}"')
|
||||
def step_pdsa_output_contains(context: Any, text: str) -> None:
|
||||
"""Assert the output contains the expected text."""
|
||||
output = context.pdsa_output
|
||||
assert text in output, f"Expected '{text}' in output but got:\n{output}"
|
||||
|
||||
|
||||
@then('the project delete spec alignment output should not contain "{text}"')
|
||||
def step_pdsa_output_not_contains(context: Any, text: str) -> None:
|
||||
"""Assert the output does not contain the given text."""
|
||||
output = context.pdsa_output
|
||||
assert text not in output, (
|
||||
f"Expected '{text}' NOT in output but it was present:\n{output}"
|
||||
)
|
||||
|
||||
|
||||
@then("the project delete spec alignment JSON success field should be true")
|
||||
def step_pdsa_json_success_true(context: Any) -> None:
|
||||
"""Parse the JSON envelope output and assert data.success is True."""
|
||||
output = context.pdsa_output.strip()
|
||||
envelope = json.loads(output)
|
||||
data = envelope.get("data", {})
|
||||
assert data.get("success") is True, (
|
||||
f"Expected data.success=True in JSON envelope but got: {envelope}"
|
||||
)
|
||||
|
||||
|
||||
@then('the project delete spec alignment JSON deleted field should be "{name}"')
|
||||
def step_pdsa_json_deleted_field(context: Any, name: str) -> None:
|
||||
"""Parse the JSON envelope output and assert data.deleted matches the project name."""
|
||||
output = context.pdsa_output.strip()
|
||||
envelope = json.loads(output)
|
||||
data = envelope.get("data", {})
|
||||
assert data.get("deleted") == name, (
|
||||
f"Expected data.deleted='{name}' in JSON envelope but got: {envelope}"
|
||||
)
|
||||
BLOCKER —
# type: ignoreis prohibited with zero tolerance.CONTRIBUTING.md prohibits all
# type: ignoresuppressions, including[import-untyped]. Examine other step files in this directory — the standard approach across the codebase is to import behave without any inline suppression:If Pyright reports an error for the untyped import, the correct fix is to declare a stub package for
behaveinpyproject.tomldev-dependencies, not to suppress inline.Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker