The invariant precedence chain is four-tier per specification §92:
plan > action > project > global
This fix updates:
1. Module docstring in invariant.py to document the correct four-tier precedence
2. InvariantScope class docstring to reflect PLAN > ACTION > PROJECT > GLOBAL
3. merge_invariants() function to accept action_invariants parameter
4. InvariantSet.merge() class method to accept and pass action_invariants
5. InvariantService.get_effective_invariants() to collect and pass action invariants
6. BDD test steps to include action invariants in merge operations
7. Benchmark suite to include action invariants in performance tests
8. Robot Framework helper to pass action_invariants to merge functions
9. CHANGELOG.md entry under [Unreleased]/### Fixed section
10. CONTRIBUTORS.md entry documenting HAL 9000 contribution
All docstrings now correctly document the four-tier precedence chain,
and the merge logic properly handles action-scope invariants between
plan and project scopes.
ISSUES CLOSED: #9003
author CleverThis <hal9000@cleverthis.com> 1776170939 +0000
committer CleverThis <hal9000@cleverthis.com> 1776170939 +0000
refactor(agent): replace hardcoded dependency and context file limits with configurable parameters
Implemented configurable limits for the agent and graph components:
- ContextAnalysisAgent now accepts max_dependencies: int = 10 with validation (ValueError if <= 0)
- _parse_dependencies uses self.max_dependencies instead of a hard-coded 10
- PlanGenerationGraph now accepts max_context_files: int = 5 with validation (ValueError if <= 0)
- _format_context_summary uses self.max_context_files instead of a hard-coded 5
- Updated class docstrings to reflect new parameters
- Added Behave feature file at features/agent_configurable_limits.feature with 12 scenarios
- Added step definitions at features/steps/agent_configurable_limits_steps.py
ISSUES CLOSED: #9050
Resource Pydantic model requires resource_id to match the ULID pattern
^[0-9A-HJKMNP-TV-Z]{26}$ and classification to be 'physical' or
'virtual'. The test fixture used '01HANDLER0000000000000001' (invalid
ULID) and 'tool' (invalid enum), causing a ValidationError during
scenario setup that Behave reported as a traceback outside scenario.
Fixes the errored scenario in features/container_clone_into.feature:88.
ISSUES CLOSED: #7555
The devcontainer_handler_protocol_methods.feature file and its step
definitions still referenced the old ContainerLifecycleState.DETECTED
terminology. Update to DISCOVERED for consistency with specification.
ISSUES CLOSED: #7555
Addresses remaining CI review feedback from HAL9001 on PR #8304:
1. Moved all cleveragents imports from inside function bodies to module-level
in features/steps/container_clone_into_steps.py (5 functions fixed)
2. Moved BUILTIN_TYPES import from inside step_look_up_in_builtin_types()
to module level in features/steps/devcontainer_sandbox_strategy_steps.py
3. Removed redundant inline import of EMPTY_CONTENT_HASH and BaseResourceHandler
inside diff() method of devcontainer.py (already available at module level)
4. Fixed stale docstring referencing old 'detected' terminology in
robot/helper_devcontainer_lifecycle.py cmd_transition_valid()
All files pass ruff format and ruff check.
The --clone-into CLI argument was registered and the helper
clone_repo_into_container() was implemented, but DevcontainerHandler.resolve()
never read the clone_into property or called the helper. This meant that
agents resource add container-instance --clone-into <url> silently ignored
the flag at runtime (acceptance criterion #2 from issue #7555 was unmet).
Wire the clone step into DevcontainerHandler.resolve(): after
activate_container() returns and the lifecycle tracker has a container_id,
validate the URL and call clone_repo_into_container(). Also add an
end-to-end BDD scenario that exercises the full handler to clone path via
mocks.
ISSUES CLOSED: #7555
- Change validate_clone_into_url() return type from bool to None
- Raise ValueError for empty or invalid git repository URLs
- Update BDD steps to catch ValueError and set clone_url_valid accordingly
- Aligns with contract requirement from PR #8304 review feedback
- Remove committed build artifacts (test_reports/summary.txt,
test_reports/test_results.json) and add test_reports/ to .gitignore
- Fix CLI resource.py: update lifecycle state condition and warning
banner from 'detected (not built)' to 'discovered (not built)' and
'Devcontainer detected' to 'Devcontainer discovered' to align with
ContainerLifecycleState.DISCOVERED rename
- Fix BDD feature file: update resource_list_lifecycle_state.feature
scenario title and assertion from 'Devcontainer detected' to
'Devcontainer discovered'
- Fix robot/helper_devcontainer_lifecycle.py: update enum value check
from 'detected' to 'discovered' in cmd_enum_values()
- Fix robot/helper_devcontainer_handler.py: update strategy check
assertion from 'none' to 'snapshot' to match PR #8304 change
- Update CONTRIBUTORS.md with HAL 9000 feature contribution entry
Closes#7555 (via PR #8304)
Fixes: CI unit_tests and integration_tests failures from PR #8304
- Adds a --clone-into option to the container-instance command to clone repository contents into a specified path during container setup.
- Fixes the devcontainer-instance sandbox strategy to ensure proper isolation, correct mount permissions, and deterministic behavior across environments.
- Updates related validation and error handling to reflect the new option and sandbox changes.
ISSUES CLOSED: #7555
Fixes the four root causes behind PR #8733's red unit_tests + Robot
integration_tests gates after the post-rebase landing of the v3.3.0
spec + invariant enforcement work.
1. Lazy-import module path used a slash instead of a dot — the
`__getattr__` lookup in `application/services/__init__.py` could
never resolve `SubplanSpawnError` because `importlib.import_module`
only accepts dotted module paths. This broke both the
`svcov3 lazy-load SubplanSpawnError` Behave scenario AND the
`Test Services Package Exports` Robot scenario (the
`from cleveragents.application.services import *` star-import walks
`__all__` and trips on the bad entry).
2. `subplan_service_coverage_boost.feature:11` contained the literal
placeholder text `{1:d}` instead of the literal value `1`. Gherkin
does not interpolate `{n:d}` in feature bodies — only in step
patterns — so the existing
`the SubplanSpawnError message should contain {n:d} semicolons`
step could not match.
3. `step_load_invariants_plan_only` used `@when(re.compile(...))`
without `use_step_matcher("re")`, so behave silently treated it as
an undefined step. Switched the file's matcher to `re` for that
single step (auto-anchored, no end marker — behave's `re` matcher
refuses `$`) and back to `parse` for the rest, so it no longer
collides with the parse-matched "with project" variant whose
`{plan_id}` field is greedy.
4. `step_check_action_against_invariants` used the parse field
`{action_text}` which doesn't match empty strings, so the
"Empty action text" scenario reported the When step as undefined.
Extracted the body into `_check_action_against_loaded` and added a
literal-pattern `'I check action "" against loaded invariants'`
step that delegates to it. Both step variants now also write
`context.error` so the shared
`the error message should contain "..."` step in `service_steps.py`
(which reads `context.error`) works against either error type.
5. `step_attempt_strategy_decision` / `step_create_strategy_decision`
only checked `context.strategize_invariants`, which is empty when
the scenario adds a global invariant via `Given` but never runs the
explicit `I start the Strategize phase` step. Added
`_strategize_active_invariants` helper that falls back to all
active invariants on the service — mirrors the same fallback
pattern already used by `_check_action_against_loaded`.
6. `step_winning_scope` in `invariant_reconciliation_actor_steps.py`
only read `context.reconciliation_result`, so reusing the
`the winning invariant for "X" should be from "Y" scope` assertion
in a non-reconciliation scenario errored with AttributeError. Now
falls back to `context.loaded_invariants` (which
`InvariantService.load_active_invariants` already merges with the
plan > project > global precedence the scenario asserts).
Verified locally:
- `unit_tests` gate passes (16504 scenarios, 0 failed, 0 errored).
- `lint` gate passes.
- `Test Services Package Exports` star-import path resolves
`SubplanSpawnError` cleanly via the package `__getattr__`.
ISSUES CLOSED: #8725
Move SubplanSpawnError from local subplan_service definition to centralized
cleveragents.core.exceptions alongside four new spec-defined error types:
SubplanExecutionError, MaxParallelExceededError, and SubplanDepthLimitError.
Per the v3.3.0 specification (AUTO-ARCH-6), all subplan-related errors are
defined in exceptions.py with proper inheritance hierarchy under DomainError/
PlanError/BusinessRuleViolation. The old local SpawnValidationError class has
been replaced with SubplanSpawnError(PlanError) with a simplified constructor
API (message string instead of validation_errors list).
Updates:
- exceptions.py: Added 4 subplan error classes + __all__ entries
- subplan_service.py: Remove local SpawnValidationError, import SubplanSpawnError from exceptions
- services/__init__.py: Update TYPE_CHECKING stub and _LAZY_IMPORTS for new location
- vulture_whitelist.py: Replace old entry with new error class names
- docs/reference/subplan_service.md: Update to reference SubplanSpawnError (v3.3.0)
- features/*.feature + steps: Update test references from SpawnValidationError to SubplanSpawnError
Add comprehensive Subplan System specification defining module boundaries,
data models (Subplan, SubplanResult, SubplanTree), PostgreSQL schema with
indexes, the 8-step spawning algorithm, concurrency control via per-plan
semaphores, and error handling.
Implement invariant loading and enforcement in Strategize phase:
- Add InvariantViolationError exception class
- Add load_active_invariants() and check_invariants() methods to InvariantService
- Add _is_violation heuristic for action/invariant matching
- Add BDD tests with @load_invariants, @check_invariants, etc. tags
ISSUES CLOSED: #8725
The step "I build the decision tree with default options" was already
defined in features/steps/plan_explain_steps.py:268. This caused
behave.step_registry.AmbiguousStep during step loading, which crashed
all 31 parallel workers before any scenario could run (0 scenarios,
31 errored at feature level).
Rename the When step in the TDD feature and its step definition to
"I build the correction TDD test decision tree" — unique across the
entire features/steps/ directory.
ISSUES CLOSED: #8576
The step_tree_json_valid BDD step was asserting a raw list from
format_output, but the function wraps all machine-readable output in
a spec-required envelope dict ({"data": [...]}). This PR fixes the
step to correctly validate the envelope structure (dict with data key)
and removes @tdd_expected_fail from the @tdd_issue_4254 scenario so
it runs as a permanent regression guard.
The code producing decision_id in tree nodes was already correct; only
the test assertion needed fixing.
ISSUES CLOSED: #9096
Apply ruff format to features/steps/auto_debug_prompt_injection_steps.py
and robot/helper_auto_debug_agent_prompt_injection.py to resolve CI lint
failures (missing blank lines between top-level definitions).
- Add _sanitize_user_input() helper that catches PromptInjectionDetected and falls back to wrap_user_content() instead of crashing the agent
- Remove dead code (_bs, _be variables) from all three agent methods
- Use wrap_user_content() for error_analysis (internal LLM output) in _generate_fix() to avoid crashing on the agent's own output
- Add @security @prompt-injection BDD tags to feature file and all scenarios
- Add missing @then("the boundary markers should be present") step definition
- Add Robot Framework integration tests (auto_debug_agent_prompt_injection.robot)
- Update CHANGELOG.md and CONTRIBUTORS.md
ISSUES CLOSED: #9110
- Import PromptSanitizer from cleveragents.application.services.prompt_sanitizer
- Create module-level _SANITIZER instance for prompt boundary markers
- Apply sanitize_and_wrap() to error_msg and code_ctx in _analyze_error()
- Apply sanitize_and_wrap() to error_analysis and code_context in _generate_fix()
- Apply sanitize_and_wrap() to error_message in _validate_fix()
- Augment system prompts with BOUNDARY_INSTRUCTION to inform LLM about markers
- Add comprehensive BDD test scenarios for prompt injection mitigation
- Add step definitions for testing boundary markers and injection attempts
Fixes#9110
Four CI failures fixed:
1. JSON/YAML progress scenarios (features/output_rendering.feature:588 and :1584):
The conflict resolver had preserved master's test assertions expecting
ProgressIndicator elements in JSON/YAML data arrays, but the PR's
_snapshot_to_dict correctly omits them per spec §26936 ("progress is
omitted from JSON output"). Removed the assertions that contradict the
spec-compliant implementation.
2. ColumnDef all-fields scenario (:1885):
Test checked for "col_type" in raw JSON output, but _column_def_to_dict
serialises the field under the key "type" (via Pydantic alias). Changed
assertion to "width_hint" which IS a serialised key in the ColumnDef dict.
3. Rich-with-cursor scenario (:2154):
Step constructed TerminalCapabilities(supports_cursor=True, term=...) using
the old field names — now backward-compat properties, not Pydantic fields.
Pydantic silently ignores unknown kwargs, leaving supports_cursor_movement=False
and causing select_materializer("rich") to return TableMaterializer. Updated
to supports_cursor_movement=True and term_program="xterm-256color".
4. Robot json-all / yaml-all helpers:
Same conflict-resolution issue as #1: helper expected all 10 element types
including "progress" in JSON/YAML data arrays. Removed "progress" from both
expected lists to match the spec-compliant implementation.
Implement the three missing architectural components of the Output Rendering
Framework as specified in issue #917:
1. RendererRegistry (spec §27249-27350): Central registry for format
(MaterializationStrategy, ElementRenderer) pairs with register(),
resolve(), available_formats(), is_registered() methods and a
FormatRegistration model. Built-in formats pre-registered in
default_registry. Replaces hardcoded if/elif chains for format
resolution.
2. ElementRenderer Protocol (spec §26557-26654): Per-element render
methods (render_panel, render_table, render_tree, etc.) plus
serialize() and can_render(). Six concrete implementations:
PlainElementRenderer, ColorElementRenderer, TableElementRenderer,
RichElementRenderer, JsonElementRenderer, YamlElementRenderer.
Each format now has a paired (Strategy, Renderer).
3. TerminalCapabilities (spec §27264-27301): Extended from 4 fields to
all 11 spec-defined fields: width, height, supports_256_color,
supports_truecolor, supports_unicode, supports_alternate_screen,
no_color, plus renames supports_cursor → supports_cursor_movement,
term → term_program. Backward-compatible properties preserved.
Additional fixes:
- ColumnDef serialises column type as 'type' (not 'col_type') per spec
§26199, with alias for backward compatibility
- YAML output uses sort_keys=True per spec §27168
- Progress elements omitted from JSON/YAML per spec §26936
- MaterializationStrategy.bind(renderer, terminal_caps) method added
to protocol and all strategy implementations (SD-19 resolved)
- Updated SD documentation in __init__.py
ISSUES CLOSED: #917
Add domain-scenario Gherkin tags to all A2A, session, and CLI feature
files (30 files) so tests can be filtered individually via behave.
- 8 A2A feature files: @a2a tag
- 7 session feature files: @session tag
- 15 CLI feature files: @cli tag
ISSUES CLOSED: #9124
- Fix error suppression in validate_all_commands: log at DEBUG level instead of silently swallowing exceptions
- Fix multi-word command name parsing bug: use command_token_count to skip the full command prefix when extracting positional values
- Add @cli tag to features/cli_docstring_example_validation.feature
- Update CONTRIBUTING.md with CLI docstring example style guide
- Update CHANGELOG.md with entry for automated docstring validation
- Update CONTRIBUTORS.md with contribution entry
- Fix test design flaws: separate Given/When/Then steps per scenario
- Add validate_all_commands test coverage via new Behave scenario
- Fix _extract_positional_args to only count required positional args
Implemented DocstringExampleValidator class in src/cleveragents/cli/docstring_validator.py that validates CLI command docstring examples to ensure positional arguments precede option flags and align with Typer signatures.
Added Behave feature file at features/cli_docstring_example_validation.feature containing scenarios for valid and invalid docstring examples and edge cases.
Added step definitions at features/steps/cli_docstring_example_validation_steps.py implementing Given/When/Then steps to drive validation and report mismatches.
Fixed the problematic docstring in src/cleveragents/cli/commands/plan.py (rollback_plan function) to reflect correct positional argument order and clearer usage.
The validator ensures that docstring examples have positional arguments before option flags, matching the actual Typer command signature.
ISSUES CLOSED: #9106
Expand multi-exception tuples in _compute_actor_impact() to one
exception type per line, satisfying ruff format's layout rules.
Collapse single-argument @when() decorator to one line where it fits.
ISSUES CLOSED: #8567
- Add return type annotation tuple[Any, Any | None] to _get_services()
- Fix _load_config_text() to catch yaml.YAMLError and TypeError instead
of ValueError/AttributeError around yaml.safe_load(), preserving the
user-friendly typer.BadParameter message for malformed YAML input
- Fix _compute_actor_impact() defensive guards to also catch
CleverAgentsError, OperationalError, and ValidationError in addition
to AttributeError/RuntimeError, keeping actor removal resilient when
the database layer is unavailable
- Update CHANGELOG.md with entry under Changed section for issue #8567
- Add features/actor_exception_handling.feature with four Behave scenarios
covering the exception handling contract changes
- Add features/steps/actor_exception_handling_steps.py with step definitions
ISSUES CLOSED: #8567
The generic EC2 fallback iterated over Reservations dicts and called
item.get("InstanceId", "") on each Reservation, which always returned ""
because Reservations have "ReservationId"/"Instances" — not "InstanceId".
Every result had id="" and a malformed ARN.
Add a dedicated handler before the generic fallback that unpacks
response["Reservations"] -> reservation["Instances"] -> InstanceId,
mirroring the pattern used for s3/ecs/eks/iam/lambda/rds.
Also add BDD scenario, step definitions, and mock helper support for
describe_instances returning N instances with non-empty IDs.
ISSUES CLOSED: #1021
Remove duplicate except ImportError blocks in cloud_resources_steps.py
(B025 violations in step_sandbox_create, step_sandbox_commit,
step_sandbox_rollback). Apply ruff format to 5 files flagged by the
format check.
- Extract AWS-specific logic into cloud_aws.py and cloud_providers.py to keep all files under the 500-line limit (cloud.py: 490 lines, cloud_aws.py: 498 lines, cloud_providers.py: 181 lines)
- Fix sandbox test regression: change cloud_resources.feature sandbox create scenario from "aws" to "gcp" provider (AWS no longer raises NotImplementedError)
- Add ImportError handling to step_sandbox_create/commit/rollback in cloud_resources_steps.py
- Remove all 9 type: ignore comments from production code using proper type narrowing and boto3/botocore type stubs in typings/
- Move plan_id validation before logger.info() in all three sandbox methods (fail-fast principle)
- Fix exception suppression: discover_aws_resources() now propagates exceptions instead of catching bare Exception
- Move deferred imports (PhysVirt, ResourceCapabilities, _derive_child_id) to module level in cloud_aws.py
- Split cloud_aws_sdk_steps.py (755 lines) into focused modules: cloud_aws_helpers.py, cloud_aws_session_steps.py, cloud_aws_discover_steps.py, cloud_aws_sandbox_steps.py
- Add CHANGELOG.md entry for AWS SDK integration feature
- Update robot/helper_cloud_resources.py to handle ImportError/ NotImplementedError for AWS sandbox operations
ISSUES CLOSED: #1021
Implements real AWS SDK integration for CloudResourceHandler using boto3
as an optional dependency. Key changes:
- Add boto3/botocore as optional [aws] dependency in pyproject.toml
- Implement CloudResourceHandler.resolve() for AWS: builds boto3 session,
verifies credentials via STS get_caller_identity for account-level types,
and returns a BoundResource with the resource ARN as sandbox_path
- Implement discover_aws_resources() to enumerate VPCs, subnets, instances,
S3 buckets, IAM roles, RDS instances, ECS clusters, Lambda functions,
and EKS clusters via the AWS API
- Implement CloudResourceHandler.discover_children() for AWS resource types
using the new discovery function
- Implement CloudSandboxStrategy.create/commit/rollback for AWS using a
tag-based isolation strategy (CleverAgents:PlanId tag)
- GCP and Azure providers still raise NotImplementedError (pending)
- boto3 is optional: handler raises ImportError with helpful install message
when boto3 is not installed
- Credentials are never logged (existing redaction infrastructure preserved)
- Update cloud_resources.feature to reflect new AWS behavior
- Add comprehensive cloud_aws_sdk.feature with 47 BDD scenarios covering
all new code paths with mocked boto3
Closes#1021
The plan correct --format json output already nests correction fields
under data.correction and sets command to "plan correct" via
format_output, so the three BDD scenarios now pass against the current
implementation. Remove the @tdd_expected_fail inversion tag so CI
reports them as passing, not as unexpected-pass failures.
Updated CHANGELOG.md and CONTRIBUTORS.md.
ISSUES CLOSED: #8584
Deferred the import of ``cleveragents.cli.commands.plan`` from module
level to a lazy helper ``_get_plan_app()``. The module triggers a
~100 s import chain (application container, all services, repositories,
etc.) when loaded cold. Loading it at step-definition import time
added that cost to every behave worker process before any scenario ran,
causing the unit_tests CI job to time out.
By deferring to the first step execution the module is already cached
in ``sys.modules`` (loaded by the many other step files that import it
at module level), so the actual cost at execution time is near-zero.
The three @tdd_expected_fail scenarios still fail with AssertionError
(data.correction.mode absent, command field empty) and the TDD
inversion hook correctly converts those failures to passes in CI.
ISSUES CLOSED: #8584
Add @tdd_expected_fail BDD scenarios that prove the plan correct --format json
command outputs a flat dict instead of the spec-required nested envelope structure.
The scenarios assert data.correction.mode is present (currently absent) and that
the command field is "plan correct" (currently empty string).
ISSUES CLOSED: #8584
Replace five # type: ignore comments with setattr() calls (for module
attribute monkey-patching) and an Any return type (for _get_memory_engines),
eliminating all type suppressions per project zero-tolerance policy.
Fix ruff format violations: collapse @when decorator to one line, reformat
raise...from split, and reformat assert message wrapping.
ISSUES CLOSED: #7566
Add MEMORY_ENGINES_LOCK to engine_cache.py and wrap the check-and-set
in UnitOfWork.engine with it to prevent concurrent threads from creating
duplicate in-memory SQLite engines. Also fix cache-hit bug where
self._engine was never assigned on a cache hit.
Closes#7566
The test scenario 'validation_summary is populated after DoD evaluation'
was expecting 'total' and 'required_passed' keys which are not added by
the DoD evaluation. These keys are only present when the plan goes through
the Execute phase validation gate. The DoD evaluation only adds
'dod_evaluated', 'dod_all_passed', and 'dod' keys.
Updated the test to check for the 'dod' key instead, which correctly
reflects the structure of the validation_summary after DoD evaluation.
ISSUES CLOSED: #7927
Before transitioning to the Apply phase, PlanLifecycleService.apply_plan
now evaluates the plan's definition_of_done criteria using DoDEvaluator
(TextMatchEvaluator). If any required criteria fail, a DoDGatingError is
raised and the plan remains in Execute/COMPLETE state. The evaluation
result is stored in plan.validation_summary with dod_evaluated=True and
dod_all_passed reflecting the outcome. Plans with no DoD text skip
evaluation and proceed normally.
Adds DoDGatingError exception class and _evaluate_dod() helper method.
Adds BDD feature file and step definitions for DoD gating scenarios.
ISSUES CLOSED: #7927
The typing.cast import was placed after third-party imports (behave),
violating ruff/isort PEP 8 ordering enforced by CI lint. Move it into
the stdlib block before the blank-line separator from third-party.
Fixes CI / lint failure on this branch. Closes#8588
Fixes a race condition (TOCTOU) in ActorLoader.list_actors() where the namespace
filter was applied outside the threading lock, creating a window for concurrent
dictionary modifications. Moving the filter inside the with self._lock: block
ensures atomic reads and filtering.
- Move namespace filtering inside the RLock in list_actors()
- Add concurrency BDD test (threading.Barrier) for list_actors + clear race condition
- Update CHANGELOG.md with fix entry (closes#8588)
- Update CONTRIBUTORS.md with contribution details
ISSUES CLOSED: #8588
- Add missing @tdd_issue companion tag to @tdd_issue_7623 scenario;
before_scenario hook raised ValueError causing hook_error on every run
- Apply ruff format to validation_pipeline.py and
validation_pipeline_concurrency_steps.py (2 files reformatted)
- Add use_step_matcher("parse") reset at end of concurrency steps
to match convention in validation_pipeline_steps.py
ISSUES CLOSED: #7623
Address all 4 remaining blocking issues from PR #7811 review:
- Delete duplicate _validation_pipeline_mock.py in features/steps/
(the inline MockValidationExecutor is kept where it belongs)
- Replace # type: ignore[return-value] with cast(dict[str, Any], ...)
to satisfy the zero-type-suppressions policy for test additions
- Add @tdd_issue_7623 regression tag on concurrency scenario
- Remove unreachable dead-code RuntimeError guard in
_install_thread_local_streams() (constructors never return None)
All CI lint checks now pass. Source files remain within line limits.
The relative import from ._validation_pipeline_mock causes behave-parallel's
module loader to create globals without '__name__', raising a KeyError during
load_step_modules(). Restoring the inline definition resolves the crash.
Closes#7811
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.
ISSUES CLOSED: #7872