docs(specification): add deleted_at field to agents project delete JSON/YAML output #8192

Merged
HAL9000 merged 1 commits from spec/add-deleted-at-field-to-project-delete into master 2026-06-02 08:26:36 +00:00
4 changed files with 202 additions and 27 deletions
+7
View File
@@ -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
+6 -27
View File
@@ -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]
Outdated
Review

BLOCKER — # type: ignore is prohibited with zero tolerance.

CONTRIBUTING.md prohibits all # type: ignore suppressions, including [import-untyped]. Examine other step files in this directory — the standard approach across the codebase is to import behave without any inline suppression:

from behave import given, then, when

If Pyright reports an error for the untyped import, the correct fix is to declare a stub package for behave in pyproject.toml dev-dependencies, not to suppress inline.


Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

**BLOCKER — `# type: ignore` is prohibited with zero tolerance.** CONTRIBUTING.md prohibits all `# type: ignore` suppressions, including `[import-untyped]`. Examine other step files in this directory — the standard approach across the codebase is to import behave without any inline suppression: ```python from behave import given, then, when ``` If Pyright reports an error for the untyped import, the correct fix is to declare a stub package for `behave` in `pyproject.toml` dev-dependencies, not to suppress inline. --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
# ---------------------------------------------------------------------------
# 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
Outdated
Review

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

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
Outdated
Review

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:

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

**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}"
)