From 76f9e37b181014d4b412c504bd7d958f3693d3bb Mon Sep 17 00:00:00 2001 From: khyari hamza Date: Wed, 11 Feb 2026 14:05:58 +0100 Subject: [PATCH 01/11] feat: add ResourceType and SandboxStrategy enums with associated tests --- features/resource_model.feature | 118 +++++++++ features/steps/resource_model_steps.py | 237 ++++++++++++++++++ .../domain/models/core/__init__.py | 3 + .../domain/models/core/resource.py | 57 +++++ 4 files changed, 415 insertions(+) create mode 100644 features/resource_model.feature create mode 100644 features/steps/resource_model_steps.py create mode 100644 src/cleveragents/domain/models/core/resource.py diff --git a/features/resource_model.feature b/features/resource_model.feature new file mode 100644 index 000000000..ee405842d --- /dev/null +++ b/features/resource_model.feature @@ -0,0 +1,118 @@ +Feature: Resource Domain Model + As a developer + I want resource types and sandbox strategies defined as domain enums + So that the system can classify project resources and determine sandboxing behavior + + # ResourceType Enum Tests (B1.3) + + Scenario: ResourceType has all required values + Then the resource types should be "git_repository, filesystem, database, api_endpoint, document_corpus, cloud_infrastructure" + + Scenario: ResourceType GIT_REPOSITORY has correct value + When I access ResourceType.GIT_REPOSITORY + Then the resource type value should be "git_repository" + + Scenario: ResourceType FILESYSTEM has correct value + When I access ResourceType.FILESYSTEM + Then the resource type value should be "filesystem" + + Scenario: ResourceType DATABASE has correct value + When I access ResourceType.DATABASE + Then the resource type value should be "database" + + Scenario: ResourceType API_ENDPOINT has correct value + When I access ResourceType.API_ENDPOINT + Then the resource type value should be "api_endpoint" + + Scenario: ResourceType DOCUMENT_CORPUS has correct value + When I access ResourceType.DOCUMENT_CORPUS + Then the resource type value should be "document_corpus" + + Scenario: ResourceType CLOUD_INFRASTRUCTURE has correct value + When I access ResourceType.CLOUD_INFRASTRUCTURE + Then the resource type value should be "cloud_infrastructure" + + Scenario: ResourceType is a string enum + When I access ResourceType.GIT_REPOSITORY + Then the resource type should be a string + + Scenario: ResourceType can be created from string value + When I create a ResourceType from string "filesystem" + Then the resource type value should be "filesystem" + + Scenario: Invalid resource type string raises error + When I try to create a ResourceType from string "invalid_type" + Then a ValueError should be raised + + # SandboxStrategy Enum Tests (B1.4) + + Scenario: SandboxStrategy has all required values + Then the sandbox strategies should be "git_worktree, copy_on_write, overlay, transaction_rollback, versioning, none" + + Scenario: SandboxStrategy GIT_WORKTREE has correct value + When I access SandboxStrategy.GIT_WORKTREE + Then the sandbox strategy value should be "git_worktree" + + Scenario: SandboxStrategy COPY_ON_WRITE has correct value + When I access SandboxStrategy.COPY_ON_WRITE + Then the sandbox strategy value should be "copy_on_write" + + Scenario: SandboxStrategy OVERLAY has correct value + When I access SandboxStrategy.OVERLAY + Then the sandbox strategy value should be "overlay" + + Scenario: SandboxStrategy TRANSACTION_ROLLBACK has correct value + When I access SandboxStrategy.TRANSACTION_ROLLBACK + Then the sandbox strategy value should be "transaction_rollback" + + Scenario: SandboxStrategy VERSIONING has correct value + When I access SandboxStrategy.VERSIONING + Then the sandbox strategy value should be "versioning" + + Scenario: SandboxStrategy NONE has correct value + When I access SandboxStrategy.NONE + Then the sandbox strategy value should be "none" + + Scenario: SandboxStrategy is a string enum + When I access SandboxStrategy.GIT_WORKTREE + Then the sandbox strategy should be a string + + # SandboxStrategy.supports_rollback Tests (B1.4b) + + Scenario: GIT_WORKTREE supports rollback + Then SandboxStrategy.GIT_WORKTREE should support rollback + + Scenario: COPY_ON_WRITE supports rollback + Then SandboxStrategy.COPY_ON_WRITE should support rollback + + Scenario: OVERLAY supports rollback + Then SandboxStrategy.OVERLAY should support rollback + + Scenario: TRANSACTION_ROLLBACK supports rollback + Then SandboxStrategy.TRANSACTION_ROLLBACK should support rollback + + Scenario: VERSIONING supports rollback + Then SandboxStrategy.VERSIONING should support rollback + + Scenario: NONE does not support rollback + Then SandboxStrategy.NONE should not support rollback + + # SandboxStrategy.is_copy_based Tests (B1.4b) + + Scenario: COPY_ON_WRITE is copy based + Then SandboxStrategy.COPY_ON_WRITE should be copy based + + Scenario: OVERLAY is copy based + Then SandboxStrategy.OVERLAY should be copy based + + Scenario: GIT_WORKTREE is not copy based + Then SandboxStrategy.GIT_WORKTREE should not be copy based + + Scenario: TRANSACTION_ROLLBACK is not copy based + Then SandboxStrategy.TRANSACTION_ROLLBACK should not be copy based + + Scenario: VERSIONING is not copy based + Then SandboxStrategy.VERSIONING should not be copy based + + Scenario: NONE is not copy based + Then SandboxStrategy.NONE should not be copy based diff --git a/features/steps/resource_model_steps.py b/features/steps/resource_model_steps.py new file mode 100644 index 000000000..e67c619ac --- /dev/null +++ b/features/steps/resource_model_steps.py @@ -0,0 +1,237 @@ +"""Step definitions for Resource domain model tests (B1.3, B1.4).""" + +from behave import then, when +from behave.runner import Context + +from cleveragents.domain.models.core.resource import ResourceType, SandboxStrategy + + +# ResourceType Steps + + +@then('the resource types should be "{expected_order}"') +def step_check_resource_type_values(context: Context, expected_order: str) -> None: + """Verify that ResourceType has all expected values in order.""" + expected = [v.strip() for v in expected_order.split(",")] + actual = [rt.value for rt in ResourceType] + assert actual == expected, f"Expected resource types {expected}, got {actual}" + + +@when("I access ResourceType.GIT_REPOSITORY") +def step_access_git_repository(context: Context) -> None: + """Access the GIT_REPOSITORY enum member.""" + context.resource_type = ResourceType.GIT_REPOSITORY + + +@when("I access ResourceType.FILESYSTEM") +def step_access_filesystem(context: Context) -> None: + """Access the FILESYSTEM enum member.""" + context.resource_type = ResourceType.FILESYSTEM + + +@when("I access ResourceType.DATABASE") +def step_access_database(context: Context) -> None: + """Access the DATABASE enum member.""" + context.resource_type = ResourceType.DATABASE + + +@when("I access ResourceType.API_ENDPOINT") +def step_access_api_endpoint(context: Context) -> None: + """Access the API_ENDPOINT enum member.""" + context.resource_type = ResourceType.API_ENDPOINT + + +@when("I access ResourceType.DOCUMENT_CORPUS") +def step_access_document_corpus(context: Context) -> None: + """Access the DOCUMENT_CORPUS enum member.""" + context.resource_type = ResourceType.DOCUMENT_CORPUS + + +@when("I access ResourceType.CLOUD_INFRASTRUCTURE") +def step_access_cloud_infrastructure(context: Context) -> None: + """Access the CLOUD_INFRASTRUCTURE enum member.""" + context.resource_type = ResourceType.CLOUD_INFRASTRUCTURE + + +@then('the resource type value should be "{expected}"') +def step_check_resource_type_value(context: Context, expected: str) -> None: + """Check the resource type value matches expected.""" + actual = context.resource_type.value + assert actual == expected, f"Expected value '{expected}', got '{actual}'" + + +@then("the resource type should be a string") +def step_check_resource_type_is_string(context: Context) -> None: + """Verify ResourceType inherits from str.""" + assert isinstance(context.resource_type, str), ( + f"Expected ResourceType to be a string, got {type(context.resource_type)}" + ) + + +@when('I create a ResourceType from string "{value}"') +def step_create_resource_type_from_string(context: Context, value: str) -> None: + """Create a ResourceType from a string value.""" + context.error = None + try: + context.resource_type = ResourceType(value) + except ValueError as e: + context.error = e + + +@when('I try to create a ResourceType from string "{value}"') +def step_try_create_resource_type_from_string(context: Context, value: str) -> None: + """Attempt to create a ResourceType from an invalid string.""" + context.error = None + try: + context.resource_type = ResourceType(value) + except ValueError as e: + context.error = e + + +@then("a ValueError should be raised") +def step_check_value_error_raised(context: Context) -> None: + """Verify a ValueError was captured.""" + assert context.error is not None, "Expected a ValueError but none was raised" + assert isinstance(context.error, ValueError), ( + f"Expected ValueError, got {type(context.error).__name__}" + ) + + +# SandboxStrategy Steps + + +@then('the sandbox strategies should be "{expected_order}"') +def step_check_sandbox_strategy_values(context: Context, expected_order: str) -> None: + """Verify that SandboxStrategy has all expected values in order.""" + expected = [v.strip() for v in expected_order.split(",")] + actual = [ss.value for ss in SandboxStrategy] + assert actual == expected, f"Expected sandbox strategies {expected}, got {actual}" + + +@when("I access SandboxStrategy.GIT_WORKTREE") +def step_access_git_worktree(context: Context) -> None: + """Access the GIT_WORKTREE enum member.""" + context.sandbox_strategy = SandboxStrategy.GIT_WORKTREE + + +@when("I access SandboxStrategy.COPY_ON_WRITE") +def step_access_copy_on_write(context: Context) -> None: + """Access the COPY_ON_WRITE enum member.""" + context.sandbox_strategy = SandboxStrategy.COPY_ON_WRITE + + +@when("I access SandboxStrategy.OVERLAY") +def step_access_overlay(context: Context) -> None: + """Access the OVERLAY enum member.""" + context.sandbox_strategy = SandboxStrategy.OVERLAY + + +@when("I access SandboxStrategy.TRANSACTION_ROLLBACK") +def step_access_transaction_rollback(context: Context) -> None: + """Access the TRANSACTION_ROLLBACK enum member.""" + context.sandbox_strategy = SandboxStrategy.TRANSACTION_ROLLBACK + + +@when("I access SandboxStrategy.VERSIONING") +def step_access_versioning(context: Context) -> None: + """Access the VERSIONING enum member.""" + context.sandbox_strategy = SandboxStrategy.VERSIONING + + +@when("I access SandboxStrategy.NONE") +def step_access_none_strategy(context: Context) -> None: + """Access the NONE enum member.""" + context.sandbox_strategy = SandboxStrategy.NONE + + +@then('the sandbox strategy value should be "{expected}"') +def step_check_sandbox_strategy_value(context: Context, expected: str) -> None: + """Check the sandbox strategy value matches expected.""" + actual = context.sandbox_strategy.value + assert actual == expected, f"Expected value '{expected}', got '{actual}'" + + +@then("the sandbox strategy should be a string") +def step_check_sandbox_strategy_is_string(context: Context) -> None: + """Verify SandboxStrategy inherits from str.""" + assert isinstance(context.sandbox_strategy, str), ( + f"Expected SandboxStrategy to be a string, got {type(context.sandbox_strategy)}" + ) + + +# SandboxStrategy.supports_rollback Tests + + +@then("SandboxStrategy.GIT_WORKTREE should support rollback") +def step_git_worktree_supports_rollback(context: Context) -> None: + """Verify GIT_WORKTREE supports rollback.""" + assert SandboxStrategy.GIT_WORKTREE.supports_rollback is True + + +@then("SandboxStrategy.COPY_ON_WRITE should support rollback") +def step_copy_on_write_supports_rollback(context: Context) -> None: + """Verify COPY_ON_WRITE supports rollback.""" + assert SandboxStrategy.COPY_ON_WRITE.supports_rollback is True + + +@then("SandboxStrategy.OVERLAY should support rollback") +def step_overlay_supports_rollback(context: Context) -> None: + """Verify OVERLAY supports rollback.""" + assert SandboxStrategy.OVERLAY.supports_rollback is True + + +@then("SandboxStrategy.TRANSACTION_ROLLBACK should support rollback") +def step_transaction_rollback_supports_rollback(context: Context) -> None: + """Verify TRANSACTION_ROLLBACK supports rollback.""" + assert SandboxStrategy.TRANSACTION_ROLLBACK.supports_rollback is True + + +@then("SandboxStrategy.VERSIONING should support rollback") +def step_versioning_supports_rollback(context: Context) -> None: + """Verify VERSIONING supports rollback.""" + assert SandboxStrategy.VERSIONING.supports_rollback is True + + +@then("SandboxStrategy.NONE should not support rollback") +def step_none_does_not_support_rollback(context: Context) -> None: + """Verify NONE does not support rollback.""" + assert SandboxStrategy.NONE.supports_rollback is False + + +# SandboxStrategy.is_copy_based Tests + + +@then("SandboxStrategy.COPY_ON_WRITE should be copy based") +def step_copy_on_write_is_copy_based(context: Context) -> None: + """Verify COPY_ON_WRITE is copy based.""" + assert SandboxStrategy.COPY_ON_WRITE.is_copy_based is True + + +@then("SandboxStrategy.OVERLAY should be copy based") +def step_overlay_is_copy_based(context: Context) -> None: + """Verify OVERLAY is copy based.""" + assert SandboxStrategy.OVERLAY.is_copy_based is True + + +@then("SandboxStrategy.GIT_WORKTREE should not be copy based") +def step_git_worktree_not_copy_based(context: Context) -> None: + """Verify GIT_WORKTREE is not copy based.""" + assert SandboxStrategy.GIT_WORKTREE.is_copy_based is False + + +@then("SandboxStrategy.TRANSACTION_ROLLBACK should not be copy based") +def step_transaction_rollback_not_copy_based(context: Context) -> None: + """Verify TRANSACTION_ROLLBACK is not copy based.""" + assert SandboxStrategy.TRANSACTION_ROLLBACK.is_copy_based is False + + +@then("SandboxStrategy.VERSIONING should not be copy based") +def step_versioning_not_copy_based(context: Context) -> None: + """Verify VERSIONING is not copy based.""" + assert SandboxStrategy.VERSIONING.is_copy_based is False + + +@then("SandboxStrategy.NONE should not be copy based") +def step_none_not_copy_based(context: Context) -> None: + """Verify NONE is not copy based.""" + assert SandboxStrategy.NONE.is_copy_based is False diff --git a/src/cleveragents/domain/models/core/__init__.py b/src/cleveragents/domain/models/core/__init__.py index 7e7698695..f7ce1b0f5 100644 --- a/src/cleveragents/domain/models/core/__init__.py +++ b/src/cleveragents/domain/models/core/__init__.py @@ -50,6 +50,7 @@ from cleveragents.domain.models.core.project import ( ProjectSettings, ProjectStats, ) +from cleveragents.domain.models.core.resource import ResourceType, SandboxStrategy __all__ = [ "ActionState", @@ -85,6 +86,8 @@ __all__ = [ "Project", "ProjectSettings", "ProjectStats", + "ResourceType", + "SandboxStrategy", "SummaryForUpdateContextParams", "User", "can_transition", diff --git a/src/cleveragents/domain/models/core/resource.py b/src/cleveragents/domain/models/core/resource.py new file mode 100644 index 000000000..5d372457a --- /dev/null +++ b/src/cleveragents/domain/models/core/resource.py @@ -0,0 +1,57 @@ +"""Resource domain model for CleverAgents. + +Defines ResourceType and SandboxStrategy enums for classifying +project resources and determining sandboxing behavior. + +Based on implementation_plan.md (B1.3, B1.4) and ADR-004 (Pydantic Validation). +""" + +from enum import Enum + + +class ResourceType(str, Enum): + """Types of resources a project can reference. + + Each resource type maps to a category of external data source + or infrastructure that a project operates on. + """ + + GIT_REPOSITORY = "git_repository" + FILESYSTEM = "filesystem" + DATABASE = "database" + API_ENDPOINT = "api_endpoint" + DOCUMENT_CORPUS = "document_corpus" + CLOUD_INFRASTRUCTURE = "cloud_infrastructure" + + +class SandboxStrategy(str, Enum): + """Strategies for isolating resource modifications during plan execution. + + Sandboxing ensures that changes made during plan execution can be + reviewed, committed, or rolled back without affecting the original resource. + """ + + GIT_WORKTREE = "git_worktree" + COPY_ON_WRITE = "copy_on_write" + OVERLAY = "overlay" + TRANSACTION_ROLLBACK = "transaction_rollback" + VERSIONING = "versioning" + NONE = "none" + + @property + def supports_rollback(self) -> bool: + """Check if this strategy supports rolling back changes. + + All strategies except NONE support rollback, since NONE means + modifications are immediate and irreversible. + """ + return self != SandboxStrategy.NONE + + @property + def is_copy_based(self) -> bool: + """Check if this strategy creates a copy of the resource. + + COPY_ON_WRITE and OVERLAY both duplicate the resource data + to a separate location for isolation. + """ + return self in (SandboxStrategy.COPY_ON_WRITE, SandboxStrategy.OVERLAY) -- 2.52.0 From 676810d62bc817725439e08fed22f2223646d436 Mon Sep 17 00:00:00 2001 From: khyari hamza Date: Wed, 11 Feb 2026 16:20:41 +0100 Subject: [PATCH 02/11] feat: Add ValidationConfig and ContextConfig models with step definitions - Introduced ValidationConfig for project validation commands including test, lint, type-check, and build commands. - Added ContextConfig for project context indexing and filtering, with support for ignore/include patterns and file size limits. - Implemented step definitions for both models in project_config_model_steps.py. - Enhanced Project model to include validation_config and context_config fields. - Created comprehensive step definitions for Resource model, including validation and manipulation steps in resource_model_steps.py. - Updated resource.py to define Resource model with necessary fields and validation. - Refactored project.py to integrate new models and maintain backward compatibility. --- features/project_config_model.feature | 121 +++++++ features/project_model.feature | 124 +++++++ features/resource_model.feature | 86 +++++ features/steps/project_config_model_steps.py | 304 ++++++++++++++++ features/steps/project_model_steps.py | 296 ++++++++++++++++ features/steps/resource_model_steps.py | 328 +++++++++++++++++- .../domain/models/core/__init__.py | 11 +- .../domain/models/core/project.py | 273 ++++++++++++++- .../domain/models/core/resource.py | 88 ++++- 9 files changed, 1617 insertions(+), 14 deletions(-) create mode 100644 features/project_config_model.feature create mode 100644 features/project_model.feature create mode 100644 features/steps/project_config_model_steps.py create mode 100644 features/steps/project_model_steps.py diff --git a/features/project_config_model.feature b/features/project_config_model.feature new file mode 100644 index 000000000..a9b2d86cc --- /dev/null +++ b/features/project_config_model.feature @@ -0,0 +1,121 @@ +Feature: Project Configuration Models + As a developer + I want ValidationConfig and ContextConfig domain models + So that projects can define validation commands and context indexing behavior + + # ValidationConfig Tests (B1.5) + + # B1.5a - Model creation with defaults + + Scenario: Create a default ValidationConfig + Given a default ValidationConfig + Then the validation test_command should be none + And the validation lint_command should be none + And the validation type_check_command should be none + And the validation build_command should be none + And the validation custom_commands should be empty + And the validation timeout_seconds should be 300 + And the validation fail_on_lint_error should be true + + # B1.5b - Fields with values + + Scenario: Create a ValidationConfig with all commands + Given a ValidationConfig with test "pytest" lint "ruff check ." typecheck "pyright" build "python -m build" + Then the validation test_command should be "pytest" + And the validation lint_command should be "ruff check ." + And the validation type_check_command should be "pyright" + And the validation build_command should be "python -m build" + + Scenario: Create a ValidationConfig with custom commands + Given a ValidationConfig with custom commands "security=bandit,docs=mkdocs build" + Then the validation custom_commands should contain "security" with value "bandit" + And the validation custom_commands should contain "docs" with value "mkdocs build" + + Scenario: Create a ValidationConfig with custom timeout + Given a ValidationConfig with timeout 600 + Then the validation timeout_seconds should be 600 + + Scenario: Create a ValidationConfig with fail_on_lint_error false + Given a ValidationConfig with fail_on_lint_error false + Then the validation fail_on_lint_error should be false + + # B1.5c - Helper methods + + Scenario: get_all_commands returns all configured commands + Given a ValidationConfig with test "pytest" lint "ruff check ." typecheck "pyright" build "python -m build" + Then get_all_commands should include "test" with value "pytest" + And get_all_commands should include "lint" with value "ruff check ." + And get_all_commands should include "type_check" with value "pyright" + And get_all_commands should include "build" with value "python -m build" + + Scenario: get_all_commands includes custom commands + Given a ValidationConfig with test "pytest" and custom commands "security=bandit" + Then get_all_commands should include "test" with value "pytest" + And get_all_commands should include "security" with value "bandit" + + Scenario: get_all_commands returns empty dict when no commands configured + Given a default ValidationConfig + Then get_all_commands should be empty + + Scenario: has_any_validation is true when commands exist + Given a ValidationConfig with test "pytest" lint "ruff check ." typecheck "pyright" build "python -m build" + Then has_any_validation should be true + + Scenario: has_any_validation is false when no commands configured + Given a default ValidationConfig + Then has_any_validation should be false + + Scenario: has_any_validation is true with only custom commands + Given a ValidationConfig with custom commands "security=bandit,docs=mkdocs build" + Then has_any_validation should be true + + # ContextConfig Tests (B1.6) + + # B1.6a - Model creation with defaults + + Scenario: Create a default ContextConfig + Given a default ContextConfig + Then the context max_file_size should be 1000000 + And the context max_files should be 100000 + And the context indexing_strategy should be "full_text" + And the context chunking_policy should be "smart" + And the context chunk_size should be 1000 + And the context include_patterns should be none + + Scenario: Default ContextConfig has default ignore patterns + Given a default ContextConfig + Then the context ignore_patterns should contain ".git/" + And the context ignore_patterns should contain "node_modules/" + And the context ignore_patterns should contain "__pycache__/" + And the context ignore_patterns should contain ".venv/" + And the context ignore_patterns should contain "*.pyc" + And the context ignore_patterns should contain ".DS_Store" + + # B1.6b - Fields with values + + Scenario: Create a ContextConfig with custom ignore patterns + Given a ContextConfig with ignore patterns "dist/,build/" + Then the context ignore_patterns should contain "dist/" + And the context ignore_patterns should contain "build/" + And the context ignore_patterns should contain ".git/" + + Scenario: Create a ContextConfig with include patterns + Given a ContextConfig with include patterns "*.py,*.ts" + Then the context include_patterns should contain "*.py" + And the context include_patterns should contain "*.ts" + + Scenario: Create a ContextConfig with custom max_file_size + Given a ContextConfig with max_file_size 2000000 + Then the context max_file_size should be 2000000 + + Scenario: Create a ContextConfig with custom indexing strategy + Given a ContextConfig with indexing_strategy "semantic" + Then the context indexing_strategy should be "semantic" + + Scenario: Create a ContextConfig with custom chunking policy + Given a ContextConfig with chunking_policy "fixed" + Then the context chunking_policy should be "fixed" + + Scenario: Create a ContextConfig with custom chunk size + Given a ContextConfig with chunk_size 500 + Then the context chunk_size should be 500 diff --git a/features/project_model.feature b/features/project_model.feature new file mode 100644 index 000000000..162fb068c --- /dev/null +++ b/features/project_model.feature @@ -0,0 +1,124 @@ +Feature: Project Domain Model Extensions + As a developer + I want the Project model to support ULID identifiers, namespaces, resources, and configs + So that projects can be properly categorized and managed + + # B1.1a - Import Resource, verify Project has new fields + + Scenario: Create a Project with new B1.1 fields + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-project" namespace "local" + Then the project project_id should be "01ARZ3NDEKTSV4RRFFQ69G5FAV" + And the project name should be "my-project" + And the project namespace should be "local" + + # B1.1b - Identity fields + + Scenario: Project has default namespace + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" and name "my-project" + Then the project namespace should be "local" + + Scenario: Project description defaults to none + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" and name "my-project" + Then the project description should be none + + Scenario: Create a Project with description + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-project" and description "A test project" + Then the project description should be "A test project" + + # B1.1c - Categorization fields + + Scenario: Project tags default to empty list + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" and name "my-project" + Then the project tags should be empty + + Scenario: Create a Project with tags + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-project" and tags "python,web,api" + Then the project tags should contain "python" + And the project tags should contain "web" + And the project tags should contain "api" + + Scenario: Project resources default to empty list + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" and name "my-project" + Then the project resources should be empty + + Scenario: Project validation_config defaults to none + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" and name "my-project" + Then the project validation_config should be none + + Scenario: Project context_config has defaults + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" and name "my-project" + Then the project context_config should not be none + + # B1.1d - Timestamp fields + + Scenario: Project has timestamps + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" and name "my-project" + Then the project created_at should be set + And the project updated_at should be set + + # B1.1e - is_remote computed property + + Scenario: Project with no resources is not remote + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" and name "my-project" + Then the project is_remote should be false + + Scenario: Project with all remote resources is remote + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-project" and all remote resources + Then the project is_remote should be true + + Scenario: Project with mixed resources is not remote + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-project" and mixed resources + Then the project is_remote should be false + + # B1.1f - Namespace validator + + Scenario: Namespace local is valid + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-project" namespace "local" + Then the project namespace should be "local" + + Scenario: Namespace with valid pattern is accepted + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-project" namespace "team_alpha" + Then the project namespace should be "team_alpha" + + Scenario: Namespace system is rejected + When I try to create a Project with namespace "system" + Then a namespace validation error should be raised + + Scenario: Namespace with uppercase is rejected + When I try to create a Project with namespace "MyNamespace" + Then a namespace validation error should be raised + + Scenario: Namespace starting with number is rejected + When I try to create a Project with namespace "1invalid" + Then a namespace validation error should be raised + + # B1.1g - Helper methods + + Scenario: namespaced_name returns correct format + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-project" namespace "team_alpha" + Then the project namespaced_name should be "team_alpha/my-project" + + Scenario: namespaced_name with local namespace + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-project" namespace "local" + Then the project namespaced_name should be "local/my-project" + + Scenario: add_resource adds a resource to the project + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" and name "my-project" + When I add a resource named "my-repo" to the project + Then the project should have 1 resource + And the project should have a resource named "my-repo" + + Scenario: remove_resource removes a resource from the project + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-project" and a resource named "my-repo" + When I remove the resource named "my-repo" from the project + Then the project resources should be empty + + Scenario: get_resource returns the correct resource + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-project" and a resource named "my-repo" + When I get the resource named "my-repo" from the project + Then the retrieved resource name should be "my-repo" + + Scenario: get_resource returns none for unknown name + Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" and name "my-project" + When I get the resource named "nonexistent" from the project + Then the retrieved resource should be none diff --git a/features/resource_model.feature b/features/resource_model.feature index ee405842d..8b0cca7cd 100644 --- a/features/resource_model.feature +++ b/features/resource_model.feature @@ -116,3 +116,89 @@ Feature: Resource Domain Model Scenario: NONE is not copy based Then SandboxStrategy.NONE should not be copy based + + # Resource Pydantic Model Tests (B1.2) + + # B1.2a - Model structure (frozen=True) + + Scenario: Create a valid Resource with all required fields + Given a Resource with required fields id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-repo" type "git_repository" location "/tmp/repo" + Then the resource name should be "my-repo" + And the resource type should be ResourceType.GIT_REPOSITORY + And the resource location should be "/tmp/repo" + + Scenario: Resource model is frozen + Given I have a valid Resource + When I try to modify the resource name + Then a validation error should be raised + + # B1.2b - All fields present with defaults + + Scenario: Resource has correct default values + Given a Resource with required fields id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-repo" type "git_repository" location "/tmp/repo" + Then the resource is_remote should be false + And the resource sandbox_strategy should be SandboxStrategy.NONE + And the resource read_only should be false + And the resource metadata should be empty + And the resource created_at should be set + + Scenario: Create a remote Resource + Given a remote Resource with id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "api-svc" type "api_endpoint" location "https://api.example.com" + Then the resource is_remote should be true + + Scenario: Create a read-only Resource + Given a read-only Resource with id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "docs" type "document_corpus" location "/data/docs" + Then the resource read_only should be true + + Scenario: Create a Resource with custom sandbox strategy + Given a Resource with id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-repo" type "git_repository" location "/tmp/repo" strategy "git_worktree" + Then the resource sandbox_strategy should be SandboxStrategy.GIT_WORKTREE + + Scenario: Create a Resource with metadata + Given a Resource with id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-repo" type "git_repository" location "/tmp/repo" metadata "branch=main,remote=origin" + Then the resource metadata should contain key "branch" with value "main" + And the resource metadata should contain key "remote" with value "origin" + + # B1.2c - Validators + + Scenario: Reject Resource with invalid ULID + When I try to create a Resource with id "not-a-ulid" name "my-repo" type "git_repository" location "/tmp/repo" + Then a validation error should be raised + + Scenario: Reject Resource with empty name + When I try to create a Resource with an empty name + Then a validation error should be raised + + Scenario: Reject Resource with empty location + When I try to create a Resource with an empty location + Then a validation error should be raised + + Scenario: Resource name is lowercased + Given a Resource with required fields id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "My-Repo" type "git_repository" location "/tmp/repo" + Then the resource name should be "my-repo" + + Scenario: Reject Resource with invalid name characters + When I try to create a Resource with id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my repo!!" type "git_repository" location "/tmp/repo" + Then a validation error should be raised + + # B1.2d - Properties + + Scenario: Resource with sandbox strategy supports sandbox + Given a Resource with id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-repo" type "git_repository" location "/tmp/repo" strategy "git_worktree" + Then the resource supports_sandbox should be true + + Scenario: Resource with NONE sandbox strategy does not support sandbox + Given a Resource with required fields id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-repo" type "git_repository" location "/tmp/repo" + Then the resource supports_sandbox should be false + + Scenario: Writable Resource can_write is true + Given a Resource with required fields id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-repo" type "git_repository" location "/tmp/repo" + Then the resource can_write should be true + + Scenario: Read-only Resource can_write is false + Given a read-only Resource with id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "docs" type "document_corpus" location "/data/docs" + Then the resource can_write should be false + + Scenario: Resource get_sandbox_path returns correct path + Given a Resource with id "01ARZ3NDEKTSV4RRFFQ69G5FAV" name "my-repo" type "git_repository" location "/tmp/repo" strategy "git_worktree" + Then the resource get_sandbox_path with base "/sandbox" should end with "my-repo" diff --git a/features/steps/project_config_model_steps.py b/features/steps/project_config_model_steps.py new file mode 100644 index 000000000..5730cf9a4 --- /dev/null +++ b/features/steps/project_config_model_steps.py @@ -0,0 +1,304 @@ +"""Step definitions for project configuration model tests (B1.5, B1.6).""" + +from __future__ import annotations + +from behave import given, then +from behave.runner import Context + +from cleveragents.domain.models.core.project import ContextConfig, ValidationConfig + + +# ValidationConfig Steps (B1.5) + + +@given("a default ValidationConfig") +def step_default_validation_config(context: Context) -> None: + """Create a ValidationConfig with all defaults.""" + context.validation_config = ValidationConfig() + + +@given( + 'a ValidationConfig with test "{test}" lint "{lint}" typecheck "{typecheck}" build "{build}"' +) +def step_validation_config_all_commands( + context: Context, test: str, lint: str, typecheck: str, build: str +) -> None: + """Create a ValidationConfig with all standard commands.""" + context.validation_config = ValidationConfig( + test_command=test, + lint_command=lint, + type_check_command=typecheck, + build_command=build, + ) + + +@given('a ValidationConfig with custom commands "{commands_str}"') +def step_validation_config_custom_commands(context: Context, commands_str: str) -> None: + """Create a ValidationConfig with custom commands parsed from key=value pairs.""" + custom = {} + for pair in commands_str.split(","): + key, value = pair.strip().split("=", 1) + custom[key.strip()] = value.strip() + context.validation_config = ValidationConfig(custom_commands=custom) + + +@given("a ValidationConfig with timeout {timeout:d}") +def step_validation_config_timeout(context: Context, timeout: int) -> None: + """Create a ValidationConfig with custom timeout.""" + context.validation_config = ValidationConfig(timeout_seconds=timeout) + + +@given("a ValidationConfig with fail_on_lint_error false") +def step_validation_config_no_lint_fail(context: Context) -> None: + """Create a ValidationConfig with fail_on_lint_error disabled.""" + context.validation_config = ValidationConfig(fail_on_lint_error=False) + + +@given('a ValidationConfig with test "{test}" and custom commands "{commands_str}"') +def step_validation_config_test_and_custom( + context: Context, test: str, commands_str: str +) -> None: + """Create a ValidationConfig with test command and custom commands.""" + custom = {} + for pair in commands_str.split(","): + key, value = pair.strip().split("=", 1) + custom[key.strip()] = value.strip() + context.validation_config = ValidationConfig( + test_command=test, custom_commands=custom + ) + + +# ValidationConfig field assertions + + +@then("the validation test_command should be none") +def step_check_test_command_none(context: Context) -> None: + """Verify test_command is None.""" + assert context.validation_config.test_command is None + + +@then('the validation test_command should be "{expected}"') +def step_check_test_command(context: Context, expected: str) -> None: + """Verify test_command value.""" + assert context.validation_config.test_command == expected, ( + f"Expected '{expected}', got '{context.validation_config.test_command}'" + ) + + +@then("the validation lint_command should be none") +def step_check_lint_command_none(context: Context) -> None: + """Verify lint_command is None.""" + assert context.validation_config.lint_command is None + + +@then('the validation lint_command should be "{expected}"') +def step_check_lint_command(context: Context, expected: str) -> None: + """Verify lint_command value.""" + assert context.validation_config.lint_command == expected, ( + f"Expected '{expected}', got '{context.validation_config.lint_command}'" + ) + + +@then("the validation type_check_command should be none") +def step_check_typecheck_command_none(context: Context) -> None: + """Verify type_check_command is None.""" + assert context.validation_config.type_check_command is None + + +@then('the validation type_check_command should be "{expected}"') +def step_check_typecheck_command(context: Context, expected: str) -> None: + """Verify type_check_command value.""" + assert context.validation_config.type_check_command == expected, ( + f"Expected '{expected}', got '{context.validation_config.type_check_command}'" + ) + + +@then("the validation build_command should be none") +def step_check_build_command_none(context: Context) -> None: + """Verify build_command is None.""" + assert context.validation_config.build_command is None + + +@then('the validation build_command should be "{expected}"') +def step_check_build_command(context: Context, expected: str) -> None: + """Verify build_command value.""" + assert context.validation_config.build_command == expected, ( + f"Expected '{expected}', got '{context.validation_config.build_command}'" + ) + + +@then("the validation custom_commands should be empty") +def step_check_custom_commands_empty(context: Context) -> None: + """Verify custom_commands is empty dict.""" + assert context.validation_config.custom_commands == {} + + +@then('the validation custom_commands should contain "{key}" with value "{value}"') +def step_check_custom_command(context: Context, key: str, value: str) -> None: + """Verify custom_commands contains expected key-value pair.""" + assert key in context.validation_config.custom_commands, ( + f"Expected key '{key}' in custom_commands" + ) + assert context.validation_config.custom_commands[key] == value, ( + f"Expected custom_commands['{key}'] = '{value}', " + f"got '{context.validation_config.custom_commands[key]}'" + ) + + +@then("the validation timeout_seconds should be {expected:d}") +def step_check_timeout(context: Context, expected: int) -> None: + """Verify timeout_seconds value.""" + assert context.validation_config.timeout_seconds == expected + + +@then("the validation fail_on_lint_error should be true") +def step_check_fail_on_lint_true(context: Context) -> None: + """Verify fail_on_lint_error is True.""" + assert context.validation_config.fail_on_lint_error is True + + +@then("the validation fail_on_lint_error should be false") +def step_check_fail_on_lint_false(context: Context) -> None: + """Verify fail_on_lint_error is False.""" + assert context.validation_config.fail_on_lint_error is False + + +# ValidationConfig method assertions + + +@then('get_all_commands should include "{key}" with value "{value}"') +def step_check_get_all_commands_key(context: Context, key: str, value: str) -> None: + """Verify get_all_commands includes expected key-value pair.""" + commands = context.validation_config.get_all_commands() + assert key in commands, ( + f"Expected key '{key}' in get_all_commands(), got {commands}" + ) + assert commands[key] == value, ( + f"Expected commands['{key}'] = '{value}', got '{commands[key]}'" + ) + + +@then("get_all_commands should be empty") +def step_check_get_all_commands_empty(context: Context) -> None: + """Verify get_all_commands returns empty dict.""" + commands = context.validation_config.get_all_commands() + assert commands == {}, f"Expected empty dict, got {commands}" + + +@then("has_any_validation should be true") +def step_check_has_validation_true(context: Context) -> None: + """Verify has_any_validation returns True.""" + assert context.validation_config.has_any_validation() is True + + +@then("has_any_validation should be false") +def step_check_has_validation_false(context: Context) -> None: + """Verify has_any_validation returns False.""" + assert context.validation_config.has_any_validation() is False + + +# ContextConfig Steps (B1.6) + + +@given("a default ContextConfig") +def step_default_context_config(context: Context) -> None: + """Create a ContextConfig with all defaults.""" + context.context_config = ContextConfig() + + +@given('a ContextConfig with ignore patterns "{patterns}"') +def step_context_config_ignore_patterns(context: Context, patterns: str) -> None: + """Create a ContextConfig with custom ignore patterns.""" + pattern_list = [p.strip() for p in patterns.split(",")] + context.context_config = ContextConfig(ignore_patterns=pattern_list) + + +@given('a ContextConfig with include patterns "{patterns}"') +def step_context_config_include_patterns(context: Context, patterns: str) -> None: + """Create a ContextConfig with include patterns.""" + pattern_list = [p.strip() for p in patterns.split(",")] + context.context_config = ContextConfig(include_patterns=pattern_list) + + +@given("a ContextConfig with max_file_size {size:d}") +def step_context_config_max_file_size(context: Context, size: int) -> None: + """Create a ContextConfig with custom max_file_size.""" + context.context_config = ContextConfig(max_file_size=size) + + +@given('a ContextConfig with indexing_strategy "{strategy}"') +def step_context_config_indexing_strategy(context: Context, strategy: str) -> None: + """Create a ContextConfig with custom indexing strategy.""" + context.context_config = ContextConfig(indexing_strategy=strategy) + + +@given('a ContextConfig with chunking_policy "{policy}"') +def step_context_config_chunking_policy(context: Context, policy: str) -> None: + """Create a ContextConfig with custom chunking policy.""" + context.context_config = ContextConfig(chunking_policy=policy) + + +@given("a ContextConfig with chunk_size {size:d}") +def step_context_config_chunk_size(context: Context, size: int) -> None: + """Create a ContextConfig with custom chunk size.""" + context.context_config = ContextConfig(chunk_size=size) + + +# ContextConfig field assertions + + +@then("the context max_file_size should be {expected:d}") +def step_check_max_file_size(context: Context, expected: int) -> None: + """Verify max_file_size value.""" + assert context.context_config.max_file_size == expected + + +@then("the context max_files should be {expected:d}") +def step_check_max_files(context: Context, expected: int) -> None: + """Verify max_files value.""" + assert context.context_config.max_files == expected + + +@then('the context indexing_strategy should be "{expected}"') +def step_check_indexing_strategy(context: Context, expected: str) -> None: + """Verify indexing_strategy value.""" + assert context.context_config.indexing_strategy == expected + + +@then('the context chunking_policy should be "{expected}"') +def step_check_chunking_policy(context: Context, expected: str) -> None: + """Verify chunking_policy value.""" + assert context.context_config.chunking_policy == expected + + +@then("the context chunk_size should be {expected:d}") +def step_check_chunk_size(context: Context, expected: int) -> None: + """Verify chunk_size value.""" + assert context.context_config.chunk_size == expected + + +@then("the context include_patterns should be none") +def step_check_include_patterns_none(context: Context) -> None: + """Verify include_patterns is None.""" + assert context.context_config.include_patterns is None + + +@then('the context ignore_patterns should contain "{pattern}"') +def step_check_ignore_pattern(context: Context, pattern: str) -> None: + """Verify ignore_patterns contains expected pattern.""" + assert pattern in context.context_config.ignore_patterns, ( + f"Expected '{pattern}' in ignore_patterns, " + f"got {context.context_config.ignore_patterns}" + ) + + +@then('the context include_patterns should contain "{pattern}"') +def step_check_include_pattern(context: Context, pattern: str) -> None: + """Verify include_patterns contains expected pattern.""" + assert context.context_config.include_patterns is not None, ( + "include_patterns is None" + ) + assert pattern in context.context_config.include_patterns, ( + f"Expected '{pattern}' in include_patterns, " + f"got {context.context_config.include_patterns}" + ) diff --git a/features/steps/project_model_steps.py b/features/steps/project_model_steps.py new file mode 100644 index 000000000..632868a30 --- /dev/null +++ b/features/steps/project_model_steps.py @@ -0,0 +1,296 @@ +"""Step definitions for Project domain model extension tests (B1.1).""" + +from __future__ import annotations + +from pathlib import Path + +from behave import given, then, when +from behave.runner import Context +from pydantic import ValidationError + +from cleveragents.domain.models.core.project import ContextConfig, Project +from cleveragents.domain.models.core.resource import ( + Resource, + ResourceType, + SandboxStrategy, +) + +VALID_ULID = "01ARZ3NDEKTSV4RRFFQ69G5FAV" +VALID_ULID_2 = "01ARZ3NDEKTSV4RRFFQ69G5FAW" +VALID_ULID_3 = "01ARZ3NDEKTSV4RRFFQ69G5FAX" + + +def _make_resource( + name: str, + is_remote: bool = False, + resource_id: str = VALID_ULID_2, +) -> Resource: + """Helper to create a test Resource.""" + return Resource( + resource_id=resource_id, + name=name, + type=ResourceType.GIT_REPOSITORY, + location="/tmp/repo" if not is_remote else "https://github.com/test", + is_remote=is_remote, + ) + + +# Project creation steps + + +@given('a Project with project_id "{pid}" name "{name}" namespace "{namespace}"') +def step_create_project_with_namespace( + context: Context, pid: str, name: str, namespace: str +) -> None: + """Create a Project with explicit namespace.""" + context.project = Project( + project_id=pid, + name=name, + namespace=namespace, + path=Path("/tmp/test"), + ) + + +@given('a Project with project_id "{pid}" and name "{name}"') +def step_create_project_minimal(context: Context, pid: str, name: str) -> None: + """Create a Project with minimal fields.""" + context.project = Project( + project_id=pid, + name=name, + path=Path("/tmp/test"), + ) + + +@given('a Project with project_id "{pid}" name "{name}" and description "{desc}"') +def step_create_project_with_description( + context: Context, pid: str, name: str, desc: str +) -> None: + """Create a Project with description.""" + context.project = Project( + project_id=pid, + name=name, + description=desc, + path=Path("/tmp/test"), + ) + + +@given('a Project with project_id "{pid}" name "{name}" and tags "{tags_str}"') +def step_create_project_with_tags( + context: Context, pid: str, name: str, tags_str: str +) -> None: + """Create a Project with tags.""" + tags = [t.strip() for t in tags_str.split(",")] + context.project = Project( + project_id=pid, + name=name, + tags=tags, + path=Path("/tmp/test"), + ) + + +@given('a Project with project_id "{pid}" name "{name}" and all remote resources') +def step_create_project_all_remote(context: Context, pid: str, name: str) -> None: + """Create a Project where all resources are remote.""" + resources = [ + _make_resource("api-svc", is_remote=True, resource_id=VALID_ULID_2), + _make_resource("cloud-db", is_remote=True, resource_id=VALID_ULID_3), + ] + context.project = Project( + project_id=pid, + name=name, + resources=resources, + path=Path("/tmp/test"), + ) + + +@given('a Project with project_id "{pid}" name "{name}" and mixed resources') +def step_create_project_mixed_resources(context: Context, pid: str, name: str) -> None: + """Create a Project with both local and remote resources.""" + resources = [ + _make_resource("local-repo", is_remote=False, resource_id=VALID_ULID_2), + _make_resource("api-svc", is_remote=True, resource_id=VALID_ULID_3), + ] + context.project = Project( + project_id=pid, + name=name, + resources=resources, + path=Path("/tmp/test"), + ) + + +@given('a Project with project_id "{pid}" name "{name}" and a resource named "{rname}"') +def step_create_project_with_resource( + context: Context, pid: str, name: str, rname: str +) -> None: + """Create a Project with one resource.""" + resource = _make_resource(rname) + context.project = Project( + project_id=pid, + name=name, + resources=[resource], + path=Path("/tmp/test"), + ) + + +# Namespace validation steps + + +@when('I try to create a Project with namespace "{namespace}"') +def step_try_create_project_bad_namespace(context: Context, namespace: str) -> None: + """Attempt to create a Project with an invalid namespace.""" + context.error = None + try: + context.project = Project( + project_id=VALID_ULID, + name="test-project", + namespace=namespace, + path=Path("/tmp/test"), + ) + except (ValidationError, ValueError) as e: + context.error = e + + +@then("a namespace validation error should be raised") +def step_check_namespace_error(context: Context) -> None: + """Verify a namespace validation error was raised.""" + assert context.error is not None, ( + "Expected a namespace validation error but none was raised" + ) + + +# Resource manipulation steps + + +@when('I add a resource named "{rname}" to the project') +def step_add_resource(context: Context, rname: str) -> None: + """Add a resource to the project.""" + resource = _make_resource(rname) + context.project = context.project.add_resource(resource) + + +@when('I remove the resource named "{rname}" from the project') +def step_remove_resource(context: Context, rname: str) -> None: + """Remove a resource from the project.""" + context.project = context.project.remove_resource(rname) + + +@when('I get the resource named "{rname}" from the project') +def step_get_resource(context: Context, rname: str) -> None: + """Get a resource by name from the project.""" + context.retrieved_resource = context.project.get_resource(rname) + + +# Project field assertions + + +@then('the project project_id should be "{expected}"') +def step_check_project_id(context: Context, expected: str) -> None: + """Verify project_id.""" + assert context.project.project_id == expected + + +# Note: "the project name should be" step is defined in service_steps.py + + +@then('the project namespace should be "{expected}"') +def step_check_project_namespace(context: Context, expected: str) -> None: + """Verify project namespace.""" + assert context.project.namespace == expected + + +@then("the project description should be none") +def step_check_project_description_none(context: Context) -> None: + """Verify description is None.""" + assert context.project.description is None + + +@then('the project description should be "{expected}"') +def step_check_project_description(context: Context, expected: str) -> None: + """Verify description value.""" + assert context.project.description == expected + + +@then("the project tags should be empty") +def step_check_project_tags_empty(context: Context) -> None: + """Verify tags is empty list.""" + assert context.project.tags == [] + + +@then('the project tags should contain "{tag}"') +def step_check_project_tag(context: Context, tag: str) -> None: + """Verify tags contains expected value.""" + assert tag in context.project.tags + + +@then("the project resources should be empty") +def step_check_project_resources_empty(context: Context) -> None: + """Verify resources is empty list.""" + assert context.project.resources == [] + + +@then("the project validation_config should be none") +def step_check_project_validation_config_none(context: Context) -> None: + """Verify validation_config is None.""" + assert context.project.validation_config is None + + +@then("the project context_config should not be none") +def step_check_project_context_config_not_none(context: Context) -> None: + """Verify context_config is set.""" + assert context.project.context_config is not None + + +@then("the project created_at should be set") +def step_check_project_created_at(context: Context) -> None: + """Verify created_at is set.""" + assert context.project.created_at is not None + + +@then("the project updated_at should be set") +def step_check_project_updated_at(context: Context) -> None: + """Verify updated_at is set.""" + assert context.project.updated_at is not None + + +@then("the project is_remote should be false") +def step_check_project_not_remote(context: Context) -> None: + """Verify project is not remote.""" + assert context.project.is_remote is False + + +@then("the project is_remote should be true") +def step_check_project_is_remote(context: Context) -> None: + """Verify project is remote.""" + assert context.project.is_remote is True + + +@then('the project namespaced_name should be "{expected}"') +def step_check_project_namespaced_name(context: Context, expected: str) -> None: + """Verify namespaced_name property.""" + assert context.project.namespaced_name == expected + + +@then("the project should have {count:d} resource") +def step_check_project_resource_count(context: Context, count: int) -> None: + """Verify resource count.""" + assert len(context.project.resources) == count + + +@then('the project should have a resource named "{rname}"') +def step_check_project_has_resource(context: Context, rname: str) -> None: + """Verify project has a resource with the given name.""" + found = context.project.get_resource(rname) + assert found is not None, f"Expected resource '{rname}' not found" + + +@then('the retrieved resource name should be "{expected}"') +def step_check_retrieved_resource_name(context: Context, expected: str) -> None: + """Verify retrieved resource name.""" + assert context.retrieved_resource is not None + assert context.retrieved_resource.name == expected + + +@then("the retrieved resource should be none") +def step_check_retrieved_resource_none(context: Context) -> None: + """Verify retrieved resource is None.""" + assert context.retrieved_resource is None diff --git a/features/steps/resource_model_steps.py b/features/steps/resource_model_steps.py index e67c619ac..93a4b46b3 100644 --- a/features/steps/resource_model_steps.py +++ b/features/steps/resource_model_steps.py @@ -1,9 +1,18 @@ -"""Step definitions for Resource domain model tests (B1.3, B1.4).""" +"""Step definitions for Resource domain model tests (B1.2, B1.3, B1.4).""" -from behave import then, when +from __future__ import annotations + +from pathlib import Path + +from behave import given, then, when from behave.runner import Context +from pydantic import ValidationError -from cleveragents.domain.models.core.resource import ResourceType, SandboxStrategy +from cleveragents.domain.models.core.resource import ( + Resource, + ResourceType, + SandboxStrategy, +) # ResourceType Steps @@ -235,3 +244,316 @@ def step_versioning_not_copy_based(context: Context) -> None: def step_none_not_copy_based(context: Context) -> None: """Verify NONE is not copy based.""" assert SandboxStrategy.NONE.is_copy_based is False + + +# Resource Pydantic Model Steps (B1.2) + +VALID_ULID = "01ARZ3NDEKTSV4RRFFQ69G5FAV" + + +@given( + 'a Resource with required fields id "{resource_id}" name "{name}" type "{rtype}" location "{location}"' +) +def step_create_resource( + context: Context, resource_id: str, name: str, rtype: str, location: str +) -> None: + """Create a Resource with required fields only.""" + context.error = None + try: + context.resource = Resource( + resource_id=resource_id, + name=name, + type=ResourceType(rtype), + location=location, + ) + except (ValidationError, ValueError) as e: + context.error = e + + +@given("I have a valid Resource") +def step_have_valid_resource(context: Context) -> None: + """Create a valid Resource for mutation testing.""" + context.resource = Resource( + resource_id=VALID_ULID, + name="my-repo", + type=ResourceType.GIT_REPOSITORY, + location="/tmp/repo", + ) + + +@when("I try to modify the resource name") +def step_try_modify_resource_name(context: Context) -> None: + """Attempt to modify a frozen model field.""" + context.error = None + try: + context.resource.name = "new-name" # type: ignore[misc] + except ValidationError as e: + context.error = e + + +@given( + 'a remote Resource with id "{resource_id}" name "{name}" type "{rtype}" location "{location}"' +) +def step_create_resource_remote( + context: Context, resource_id: str, name: str, rtype: str, location: str +) -> None: + """Create a remote Resource.""" + context.error = None + try: + context.resource = Resource( + resource_id=resource_id, + name=name, + type=ResourceType(rtype), + location=location, + is_remote=True, + ) + except (ValidationError, ValueError) as e: + context.error = e + + +@given( + 'a read-only Resource with id "{resource_id}" name "{name}" type "{rtype}" location "{location}"' +) +def step_create_resource_read_only( + context: Context, resource_id: str, name: str, rtype: str, location: str +) -> None: + """Create a read-only Resource.""" + context.error = None + try: + context.resource = Resource( + resource_id=resource_id, + name=name, + type=ResourceType(rtype), + location=location, + read_only=True, + ) + except (ValidationError, ValueError) as e: + context.error = e + + +@given( + 'a Resource with id "{resource_id}" name "{name}" type "{rtype}" location "{location}" strategy "{strategy}"' +) +def step_create_resource_with_strategy( + context: Context, + resource_id: str, + name: str, + rtype: str, + location: str, + strategy: str, +) -> None: + """Create a Resource with a specific sandbox strategy.""" + context.error = None + try: + context.resource = Resource( + resource_id=resource_id, + name=name, + type=ResourceType(rtype), + location=location, + sandbox_strategy=SandboxStrategy(strategy), + ) + except (ValidationError, ValueError) as e: + context.error = e + + +@given( + 'a Resource with id "{resource_id}" name "{name}" type "{rtype}" location "{location}" metadata "{meta_str}"' +) +def step_create_resource_with_metadata( + context: Context, + resource_id: str, + name: str, + rtype: str, + location: str, + meta_str: str, +) -> None: + """Create a Resource with metadata parsed from key=value pairs.""" + metadata = {} + for pair in meta_str.split(","): + key, value = pair.strip().split("=", 1) + metadata[key.strip()] = value.strip() + context.error = None + try: + context.resource = Resource( + resource_id=resource_id, + name=name, + type=ResourceType(rtype), + location=location, + metadata=metadata, + ) + except (ValidationError, ValueError) as e: + context.error = e + + +@when( + 'I try to create a Resource with id "{resource_id}" name "{name}" type "{rtype}" location "{location}"' +) +def step_try_create_resource( + context: Context, resource_id: str, name: str, rtype: str, location: str +) -> None: + """Attempt to create a Resource that may fail validation.""" + context.error = None + try: + context.resource = Resource( + resource_id=resource_id, + name=name, + type=ResourceType(rtype), + location=location, + ) + except (ValidationError, ValueError) as e: + context.error = e + + +@when("I try to create a Resource with an empty name") +def step_try_create_resource_empty_name(context: Context) -> None: + """Attempt to create a Resource with empty name.""" + context.error = None + try: + context.resource = Resource( + resource_id=VALID_ULID, + name="", + type=ResourceType.GIT_REPOSITORY, + location="/tmp/repo", + ) + except (ValidationError, ValueError) as e: + context.error = e + + +@when("I try to create a Resource with an empty location") +def step_try_create_resource_empty_location(context: Context) -> None: + """Attempt to create a Resource with empty location.""" + context.error = None + try: + context.resource = Resource( + resource_id=VALID_ULID, + name="my-repo", + type=ResourceType.GIT_REPOSITORY, + location="", + ) + except (ValidationError, ValueError) as e: + context.error = e + + +# Resource field assertion steps + + +@then('the resource name should be "{expected}"') +def step_check_resource_name(context: Context, expected: str) -> None: + """Verify resource name.""" + assert context.resource.name == expected, ( + f"Expected name '{expected}', got '{context.resource.name}'" + ) + + +@then("the resource type should be ResourceType.GIT_REPOSITORY") +def step_check_resource_type_git(context: Context) -> None: + """Verify resource type is GIT_REPOSITORY.""" + assert context.resource.type == ResourceType.GIT_REPOSITORY + + +@then('the resource location should be "{expected}"') +def step_check_resource_location(context: Context, expected: str) -> None: + """Verify resource location.""" + assert context.resource.location == expected, ( + f"Expected location '{expected}', got '{context.resource.location}'" + ) + + +@then("the resource is_remote should be false") +def step_check_resource_not_remote(context: Context) -> None: + """Verify resource is not remote.""" + assert context.resource.is_remote is False + + +@then("the resource is_remote should be true") +def step_check_resource_is_remote(context: Context) -> None: + """Verify resource is remote.""" + assert context.resource.is_remote is True + + +@then("the resource sandbox_strategy should be SandboxStrategy.NONE") +def step_check_resource_strategy_none(context: Context) -> None: + """Verify default sandbox strategy is NONE.""" + assert context.resource.sandbox_strategy == SandboxStrategy.NONE + + +@then("the resource sandbox_strategy should be SandboxStrategy.GIT_WORKTREE") +def step_check_resource_strategy_worktree(context: Context) -> None: + """Verify sandbox strategy is GIT_WORKTREE.""" + assert context.resource.sandbox_strategy == SandboxStrategy.GIT_WORKTREE + + +@then("the resource read_only should be false") +def step_check_resource_not_readonly(context: Context) -> None: + """Verify resource is not read-only.""" + assert context.resource.read_only is False + + +@then("the resource read_only should be true") +def step_check_resource_is_readonly(context: Context) -> None: + """Verify resource is read-only.""" + assert context.resource.read_only is True + + +@then("the resource metadata should be empty") +def step_check_resource_metadata_empty(context: Context) -> None: + """Verify resource metadata is empty dict.""" + assert context.resource.metadata == {}, ( + f"Expected empty metadata, got {context.resource.metadata}" + ) + + +@then("the resource created_at should be set") +def step_check_resource_created_at(context: Context) -> None: + """Verify resource has a created_at timestamp.""" + assert context.resource.created_at is not None + + +@then('the resource metadata should contain key "{key}" with value "{value}"') +def step_check_resource_metadata_key(context: Context, key: str, value: str) -> None: + """Verify resource metadata contains expected key-value pair.""" + assert key in context.resource.metadata, ( + f"Expected key '{key}' in metadata, got {context.resource.metadata}" + ) + assert context.resource.metadata[key] == value, ( + f"Expected metadata['{key}'] = '{value}', got '{context.resource.metadata[key]}'" + ) + + +# Note: "a validation error should be raised" step is defined in domain_models_steps.py + + +# Resource property assertion steps + + +@then("the resource supports_sandbox should be true") +def step_check_supports_sandbox_true(context: Context) -> None: + """Verify resource supports sandbox.""" + assert context.resource.supports_sandbox is True + + +@then("the resource supports_sandbox should be false") +def step_check_supports_sandbox_false(context: Context) -> None: + """Verify resource does not support sandbox.""" + assert context.resource.supports_sandbox is False + + +@then("the resource can_write should be true") +def step_check_can_write_true(context: Context) -> None: + """Verify resource can be written to.""" + assert context.resource.can_write is True + + +@then("the resource can_write should be false") +def step_check_can_write_false(context: Context) -> None: + """Verify resource cannot be written to.""" + assert context.resource.can_write is False + + +@then('the resource get_sandbox_path with base "{base}" should end with "{suffix}"') +def step_check_sandbox_path(context: Context, base: str, suffix: str) -> None: + """Verify get_sandbox_path returns expected path.""" + result = context.resource.get_sandbox_path(Path(base)) + assert str(result).endswith(suffix), ( + f"Expected path ending with '{suffix}', got '{result}'" + ) diff --git a/src/cleveragents/domain/models/core/__init__.py b/src/cleveragents/domain/models/core/__init__.py index f7ce1b0f5..a3ceb439f 100644 --- a/src/cleveragents/domain/models/core/__init__.py +++ b/src/cleveragents/domain/models/core/__init__.py @@ -46,11 +46,17 @@ from cleveragents.domain.models.core.plan_legacy import ( PlanStatus, ) from cleveragents.domain.models.core.project import ( + ContextConfig, Project, ProjectSettings, ProjectStats, + ValidationConfig, +) +from cleveragents.domain.models.core.resource import ( + Resource, + ResourceType, + SandboxStrategy, ) -from cleveragents.domain.models.core.resource import ResourceType, SandboxStrategy __all__ = [ "ActionState", @@ -59,6 +65,7 @@ __all__ = [ "ChangeSet", "CloudBillingFields", "Context", + "ContextConfig", "ContextFile", "ContextType", "ContextUpdateResult", @@ -86,9 +93,11 @@ __all__ = [ "Project", "ProjectSettings", "ProjectStats", + "Resource", "ResourceType", "SandboxStrategy", "SummaryForUpdateContextParams", "User", + "ValidationConfig", "can_transition", ] diff --git a/src/cleveragents/domain/models/core/project.py b/src/cleveragents/domain/models/core/project.py index 7dc1a7b8a..ec8990b33 100644 --- a/src/cleveragents/domain/models/core/project.py +++ b/src/cleveragents/domain/models/core/project.py @@ -1,13 +1,142 @@ """Project domain model for CleverAgents. -Based on Phase 0 discovery and ADR-004 (Pydantic Validation). +Includes ValidationConfig (B1.5), ContextConfig (B1.6), and the Project model (B1.1). +Based on Phase 0 discovery, implementation_plan.md, and ADR-004 (Pydantic Validation). """ +from __future__ import annotations + +import re from datetime import datetime from pathlib import Path from pydantic import BaseModel, ConfigDict, Field, field_validator +from cleveragents.domain.models.core.resource import Resource + +ULID_PATTERN = r"^[0-9A-HJKMNP-TV-Z]{26}$" +NAMESPACE_PATTERN = r"^(local|[a-z][a-z0-9_]{0,49})$" +RESERVED_NAMESPACES = frozenset({"system", "internal", "admin", "root"}) + +DEFAULT_IGNORE_PATTERNS: list[str] = [ + ".git/", + "node_modules/", + "__pycache__/", + ".venv/", + "*.pyc", + ".DS_Store", +] + + +class ValidationConfig(BaseModel): + """Configuration for project validation commands. + + Defines test, lint, type-check, and build commands along with + custom commands and execution settings. + + Implements B1.5 from the implementation plan. + """ + + test_command: str | None = Field(None, description="Command to run tests") + lint_command: str | None = Field(None, description="Command to run linting") + type_check_command: str | None = Field( + None, description="Command to run type checking" + ) + build_command: str | None = Field(None, description="Command to build the project") + custom_commands: dict[str, str] = Field( + default_factory=dict, + description="Custom named validation commands", + ) + timeout_seconds: int = Field( + 300, description="Timeout for each validation command in seconds" + ) + fail_on_lint_error: bool = Field( + True, description="Whether lint errors should fail validation" + ) + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + ) + + def get_all_commands(self) -> dict[str, str]: + """Return all configured commands as a name->command dict. + + Returns: + Dictionary mapping command names to command strings. + Includes standard commands (test, lint, type_check, build) + and any custom commands. + """ + commands: dict[str, str] = {} + if self.test_command is not None: + commands["test"] = self.test_command + if self.lint_command is not None: + commands["lint"] = self.lint_command + if self.type_check_command is not None: + commands["type_check"] = self.type_check_command + if self.build_command is not None: + commands["build"] = self.build_command + commands.update(self.custom_commands) + return commands + + def has_any_validation(self) -> bool: + """Check if any validation commands are configured. + + Returns: + True if at least one command (standard or custom) is set. + """ + return len(self.get_all_commands()) > 0 + + +class ContextConfig(BaseModel): + """Configuration for project context indexing and filtering. + + Controls how project files are indexed, chunked, and filtered + for inclusion in AI context windows. + + Implements B1.6 from the implementation plan. + """ + + ignore_patterns: list[str] = Field( + default_factory=lambda: list(DEFAULT_IGNORE_PATTERNS), + description="File patterns to ignore during indexing", + ) + include_patterns: list[str] | None = Field( + None, description="File patterns to include (None means include all)" + ) + max_file_size: int = Field( + 1_000_000, description="Maximum file size in bytes (1MB default)" + ) + max_files: int = Field(100_000, description="Maximum number of files to index") + indexing_strategy: str = Field( + "full_text", description="Indexing strategy: full_text, semantic, etc." + ) + chunking_policy: str = Field( + "smart", description="Chunking policy: smart, fixed, etc." + ) + chunk_size: int = Field(1000, description="Chunk size in tokens") + + @field_validator("ignore_patterns") + @classmethod + def merge_default_ignore_patterns( + cls: type[ContextConfig], v: list[str] + ) -> list[str]: + """Merge user-provided ignore patterns with defaults. + + Ensures default patterns like .git/, node_modules/, etc. + are always included. + """ + merged = list(DEFAULT_IGNORE_PATTERNS) + for pattern in v: + if pattern not in merged: + merged.append(pattern) + return merged + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + ) + class ProjectSettings(BaseModel): """Project-specific settings and configuration.""" @@ -52,19 +181,50 @@ class Project(BaseModel): """Domain model for a CleverAgents project. A project represents a workspace with plans, contexts, and changes. + Projects can have resources, validation configs, and context configs. + + Extends original model with B1.1 fields: project_id, namespace, + description, tags, resources, validation_config, context_config. """ - id: int | None = Field(None, description="Project ID") - name: str = Field(..., min_length=1, max_length=255) + # Legacy fields (preserved for backward compatibility) + id: int | None = Field(None, description="Legacy integer project ID") path: Path = Field(..., description="Project root path") - created_at: datetime = Field(default_factory=datetime.now) - updated_at: datetime = Field(default_factory=datetime.now) settings: ProjectSettings = Field(default_factory=lambda: ProjectSettings()) # type: ignore current_plan_id: int | None = Field(None) + # B1.1b - Identity fields + project_id: str | None = Field( + None, description="Unique ULID identifier", pattern=ULID_PATTERN + ) + name: str = Field(..., min_length=1, max_length=255) + namespace: str = Field("local", description="Project namespace for grouping") + description: str | None = Field( + None, description="Human-readable project description" + ) + + # B1.1c - Categorization fields + tags: list[str] = Field( + default_factory=list, description="Project tags for filtering" + ) + resources: list[Resource] = Field( + default_factory=list, description="Project resource references" + ) + validation_config: ValidationConfig | None = Field( + None, description="Validation command configuration" + ) + context_config: ContextConfig = Field( + default_factory=ContextConfig, + description="Context indexing and filtering configuration", + ) + + # B1.1d - Timestamps + created_at: datetime = Field(default_factory=datetime.now) + updated_at: datetime = Field(default_factory=datetime.now) + @field_validator("name") @classmethod - def validate_name(cls: type["Project"], v: str) -> str: + def validate_name(cls: type[Project], v: str) -> str: """Validate project name.""" if not v.replace("-", "").replace("_", "").replace(" ", "").isalnum(): raise ValueError( @@ -72,9 +232,31 @@ class Project(BaseModel): ) return v + @field_validator("namespace") + @classmethod + def validate_namespace(cls: type[Project], v: str) -> str: + """Validate namespace format and reject reserved names. + + Namespace must match pattern: 'local' or start with lowercase letter + followed by up to 49 lowercase alphanumeric/underscore chars. + Reserved names (system, internal, admin, root) are rejected. + """ + if not re.match(NAMESPACE_PATTERN, v): + raise ValueError( + f"Namespace '{v}' must match pattern: 'local' or " + "start with lowercase letter followed by lowercase " + "alphanumeric/underscore characters (max 50 chars)" + ) + if v in RESERVED_NAMESPACES: + raise ValueError( + f"Namespace '{v}' is reserved. " + f"Reserved namespaces: {', '.join(sorted(RESERVED_NAMESPACES))}" + ) + return v + @field_validator("path") @classmethod - def validate_path(cls: type["Project"], v: Path) -> Path: + def validate_path(cls: type[Project], v: Path) -> Path: """Ensure path is absolute.""" return v.resolve() @@ -83,3 +265,80 @@ class Project(BaseModel): validate_assignment=True, arbitrary_types_allowed=True, # Allow Path ) + + # B1.1e - Computed property + @property + def is_remote(self) -> bool: + """Check if all project resources are remote. + + A project is remote only if it has resources and ALL of them + are remotely accessible. Empty resource lists return False. + """ + if not self.resources: + return False + return all(r.is_remote for r in self.resources) + + # B1.1g - Helper methods + @property + def namespaced_name(self) -> str: + """Return the fully qualified namespace/name string.""" + return f"{self.namespace}/{self.name}" + + @staticmethod + def parse_namespaced_name(namespaced: str) -> tuple[str, str]: + """Parse a 'namespace/name' string into (namespace, name). + + Args: + namespaced: String in 'namespace/name' format. + + Returns: + Tuple of (namespace, name). + + Raises: + ValueError: If the string doesn't contain exactly one '/'. + """ + parts = namespaced.split("/", 1) + if len(parts) != 2: # noqa: PLR2004 + raise ValueError(f"Expected 'namespace/name' format, got '{namespaced}'") + return parts[0], parts[1] + + def add_resource(self, resource: Resource) -> Project: + """Return a new Project with the resource added. + + Since the model may be frozen or validated, this returns + a new instance with the resource appended. + + Args: + resource: The Resource to add. + + Returns: + New Project instance with the resource added. + """ + new_resources = list(self.resources) + [resource] + return self.model_copy(update={"resources": new_resources}) + + def remove_resource(self, name: str) -> Project: + """Return a new Project with the named resource removed. + + Args: + name: Name of the resource to remove. + + Returns: + New Project instance without the named resource. + """ + new_resources = [r for r in self.resources if r.name != name] + return self.model_copy(update={"resources": new_resources}) + + def get_resource(self, name: str) -> Resource | None: + """Get a resource by name. + + Args: + name: Name of the resource to find. + + Returns: + The Resource if found, None otherwise. + """ + for r in self.resources: + if r.name == name: + return r + return None diff --git a/src/cleveragents/domain/models/core/resource.py b/src/cleveragents/domain/models/core/resource.py index 5d372457a..f268ebc13 100644 --- a/src/cleveragents/domain/models/core/resource.py +++ b/src/cleveragents/domain/models/core/resource.py @@ -1,12 +1,22 @@ """Resource domain model for CleverAgents. -Defines ResourceType and SandboxStrategy enums for classifying -project resources and determining sandboxing behavior. +Defines ResourceType enum, SandboxStrategy enum, and the Resource +Pydantic model for classifying project resources and determining +sandboxing behavior. -Based on implementation_plan.md (B1.3, B1.4) and ADR-004 (Pydantic Validation). +Based on implementation_plan.md (B1.2, B1.3, B1.4) and ADR-004 (Pydantic Validation). """ +from __future__ import annotations + +from datetime import datetime from enum import Enum +from pathlib import Path +from typing import Any + +from pydantic import BaseModel, ConfigDict, Field, field_validator + +ULID_PATTERN = r"^[0-9A-HJKMNP-TV-Z]{26}$" class ResourceType(str, Enum): @@ -55,3 +65,75 @@ class SandboxStrategy(str, Enum): to a separate location for isolation. """ return self in (SandboxStrategy.COPY_ON_WRITE, SandboxStrategy.OVERLAY) + + +class Resource(BaseModel): + """A project resource reference. + + Resources represent external data sources or repositories + that a project operates on. Each resource has a type, location, + and sandboxing strategy that determines how modifications are isolated. + + Implements B1.2 from the implementation plan. + """ + + resource_id: str = Field( + ..., description="Unique ULID identifier", pattern=ULID_PATTERN + ) + name: str = Field( + ..., min_length=1, max_length=255, description="Human-readable name" + ) + type: ResourceType = Field(..., description="Resource classification") + location: str = Field(..., min_length=1, description="Path or URI to the resource") + is_remote: bool = Field( + False, description="Whether the resource is remotely accessible" + ) + sandbox_strategy: SandboxStrategy = Field( + SandboxStrategy.NONE, + description="Strategy for isolating modifications", + ) + read_only: bool = Field(False, description="Whether the resource is read-only") + metadata: dict[str, Any] = Field( + default_factory=dict, + description="Arbitrary key-value metadata for the resource", + ) + created_at: datetime = Field( + default_factory=datetime.now, + description="Timestamp when the resource was created", + ) + + @field_validator("name") + @classmethod + def validate_name(cls: type[Resource], v: str) -> str: + """Enforce naming rules: alphanumeric with hyphens/underscores, lowercased.""" + if not v.replace("-", "").replace("_", "").isalnum(): + raise ValueError("Name must be alphanumeric with hyphens/underscores only") + return v.lower() + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + use_enum_values=False, + frozen=True, + ) + + @property + def supports_sandbox(self) -> bool: + """Check if this resource has a sandbox strategy other than NONE.""" + return self.sandbox_strategy != SandboxStrategy.NONE + + @property + def can_write(self) -> bool: + """Check if this resource allows write operations.""" + return not self.read_only + + def get_sandbox_path(self, base_path: Path) -> Path: + """Get the sandbox directory path for this resource. + + Args: + base_path: The base directory for sandboxes. + + Returns: + Path combining base_path with the resource name. + """ + return base_path / self.name -- 2.52.0 From 5a2953b99c11b2e9909f40c99288f093938a4fed Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Freeman Date: Wed, 11 Feb 2026 08:02:14 -0500 Subject: [PATCH 03/11] Docs: Updated implementation plan with the new specification --- B1_SUMMARY.md | 284 + CURRENT_TASK.md | 114 + HAMZA_PROGRESS.md | 213 + IMPLEMENTATION_PLAYBOOK.md | 718 +++ implementation_plan.md | 10569 ++++++++++++++++++++++++++++------- notes.md | 12 + 6 files changed, 9759 insertions(+), 2151 deletions(-) create mode 100644 B1_SUMMARY.md create mode 100644 CURRENT_TASK.md create mode 100644 HAMZA_PROGRESS.md create mode 100644 IMPLEMENTATION_PLAYBOOK.md create mode 100644 notes.md diff --git a/B1_SUMMARY.md b/B1_SUMMARY.md new file mode 100644 index 000000000..d85faec46 --- /dev/null +++ b/B1_SUMMARY.md @@ -0,0 +1,284 @@ +# B1 - Project Data Model: Implementation Summary + +> **Stage:** B1 (Phase 1: Foundation) +> **Status:** COMPLETE +> **Completed:** 2026-02-11 +> **Tasks:** 24/24 (6 top-level, 18 subtasks) + +--- + +## PR Summary + +### `feat(domain): add project data model foundation (B1)` + +#### Summary + +- Implement `ResourceType` (6 values) and `SandboxStrategy` (6 values + helper properties) enums for classifying project resources and sandboxing behavior +- Add `Resource` Pydantic model with ULID validation, frozen immutability, and computed properties (`supports_sandbox`, `can_write`, `get_sandbox_path`) +- Add `ValidationConfig` model for project validation commands with `get_all_commands()` and `has_any_validation()` helpers +- Add `ContextConfig` model for context indexing/filtering with default ignore patterns (`.git/`, `node_modules/`, etc.) that merge with user-provided patterns +- Extend the existing `Project` model with `project_id` (ULID), `namespace` (validated, reserved names rejected), `description`, `tags`, `resources`, `validation_config`, and `context_config` -- all with defaults for full backward compatibility with existing code + +#### Test Coverage + +90 BDD scenarios across 3 feature files, 211 steps, all passing. Full regression suite: 1702/1703 scenarios pass (1 pre-existing failure unrelated to this change). + +#### Changed Files + +| File | Change | +|------|--------| +| `src/cleveragents/domain/models/core/resource.py` | Added `Resource` model, ULID pattern constant | +| `src/cleveragents/domain/models/core/project.py` | Added `ValidationConfig`, `ContextConfig`; extended `Project` with B1.1 fields and helpers | +| `src/cleveragents/domain/models/core/__init__.py` | Exported `Resource`, `ValidationConfig`, `ContextConfig` | +| `features/resource_model.feature` | 47 scenarios for enums + Resource model | +| `features/project_config_model.feature` | 19 scenarios for ValidationConfig + ContextConfig | +| `features/project_model.feature` | 24 scenarios for Project extensions | +| `features/steps/resource_model_steps.py` | Step definitions for resource tests | +| `features/steps/project_config_model_steps.py` | Step definitions for config tests | +| `features/steps/project_model_steps.py` | Step definitions for project tests | + +#### Unblocks + +This completes the foundation layer. The following stages are now unblocked: +- **B2** -- Project CLI Commands +- **B5** -- Project Persistence (Alembic migrations + repositories) +- **B3.3-B3.4** -- Sandbox Implementations (also needs Luis's B3.1-B3.2) + +--- + +## Overview + +Stage B1 establishes the core domain models for projects and resources in CleverAgents. These are the foundational data structures that all downstream stages (CLI, persistence, services, sandboxing) depend on. + +All models follow **ADR-004 (Pydantic Validation)** and were built using the **BDD-First** workflow defined in `IMPLEMENTATION_PLAYBOOK.md`. + +--- + +## What Was Implemented + +### B1.3 - `ResourceType` Enum + +**File:** `src/cleveragents/domain/models/core/resource.py:22-34` + +A `str` enum classifying the six types of external data sources a project can reference: + +| Value | Description | +|-------|-------------| +| `GIT_REPOSITORY` | Git-based source code repository | +| `FILESYSTEM` | Local or mounted filesystem directory | +| `DATABASE` | Database connection | +| `API_ENDPOINT` | Remote API service | +| `DOCUMENT_CORPUS` | Collection of documents | +| `CLOUD_INFRASTRUCTURE` | Cloud provider resources | + +String-based (`str, Enum`) so values serialize naturally to JSON and can be compared as strings. + +--- + +### B1.4 - `SandboxStrategy` Enum + +**File:** `src/cleveragents/domain/models/core/resource.py:37-67` + +A `str` enum defining how resource modifications are isolated during plan execution: + +| Value | `supports_rollback` | `is_copy_based` | +|-------|:-------------------:|:---------------:| +| `GIT_WORKTREE` | True | False | +| `COPY_ON_WRITE` | True | True | +| `OVERLAY` | True | True | +| `TRANSACTION_ROLLBACK` | True | False | +| `VERSIONING` | True | False | +| `NONE` | False | False | + +**Helper properties:** +- `supports_rollback` -- True for all strategies except `NONE` +- `is_copy_based` -- True only for `COPY_ON_WRITE` and `OVERLAY` + +--- + +### B1.2 - `Resource` Pydantic Model + +**File:** `src/cleveragents/domain/models/core/resource.py:70-139` + +An immutable (`frozen=True`) Pydantic model representing a single project resource. + +**Fields:** + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `resource_id` | `str` | required | ULID (26 chars, pattern-validated) | +| `name` | `str` | required | Alphanumeric + hyphens/underscores, auto-lowercased | +| `type` | `ResourceType` | required | Resource classification enum | +| `location` | `str` | required | Path or URI (min_length=1) | +| `is_remote` | `bool` | `False` | Whether remotely accessible | +| `sandbox_strategy` | `SandboxStrategy` | `NONE` | Isolation strategy | +| `read_only` | `bool` | `False` | Write protection flag | +| `metadata` | `dict[str, Any]` | `{}` | Arbitrary key-value metadata | +| `created_at` | `datetime` | `now()` | Creation timestamp | + +**Validators:** +- `resource_id` -- must match ULID pattern `^[0-9A-HJKMNP-TV-Z]{26}$` +- `name` -- alphanumeric with hyphens/underscores only, auto-lowercased +- `location` -- non-empty string + +**Computed properties:** +- `supports_sandbox` -- True if `sandbox_strategy != NONE` +- `can_write` -- True if `not read_only` +- `get_sandbox_path(base_path)` -- returns `base_path / name` + +--- + +### B1.5 - `ValidationConfig` Model + +**File:** `src/cleveragents/domain/models/core/project.py:31-88` + +Configuration for project validation commands used during plan execution. + +**Fields:** + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `test_command` | `str \| None` | `None` | Test runner command | +| `lint_command` | `str \| None` | `None` | Linter command | +| `type_check_command` | `str \| None` | `None` | Type checker command | +| `build_command` | `str \| None` | `None` | Build command | +| `custom_commands` | `dict[str, str]` | `{}` | Named custom validation commands | +| `timeout_seconds` | `int` | `300` | Per-command timeout | +| `fail_on_lint_error` | `bool` | `True` | Whether lint errors block validation | + +**Methods:** +- `get_all_commands()` -- returns a `dict[str, str]` of all configured commands (standard + custom), keyed by name (`test`, `lint`, `type_check`, `build`, plus custom keys) +- `has_any_validation()` -- returns `True` if any command is configured + +--- + +### B1.6 - `ContextConfig` Model + +**File:** `src/cleveragents/domain/models/core/project.py:91-138` + +Configuration for how project files are indexed and filtered for AI context windows. + +**Fields:** + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `ignore_patterns` | `list[str]` | (see below) | File patterns to exclude | +| `include_patterns` | `list[str] \| None` | `None` | File patterns to include (None = all) | +| `max_file_size` | `int` | `1,000,000` | Max file size in bytes (1MB) | +| `max_files` | `int` | `100,000` | Max files to index | +| `indexing_strategy` | `str` | `"full_text"` | Indexing approach | +| `chunking_policy` | `str` | `"smart"` | How files are chunked | +| `chunk_size` | `int` | `1000` | Chunk size in tokens | + +**Default ignore patterns** (always merged in): +``` +.git/, node_modules/, __pycache__/, .venv/, *.pyc, .DS_Store +``` + +User-provided patterns are appended to defaults via `field_validator`, ensuring the defaults are never lost. + +--- + +### B1.1 - `Project` Model Extensions + +**File:** `src/cleveragents/domain/models/core/project.py:180-344` + +The existing `Project` model was extended with new fields while preserving full backward compatibility with legacy code. + +**New fields added:** + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `project_id` | `str \| None` | `None` | ULID identifier (pattern-validated) | +| `namespace` | `str` | `"local"` | Project namespace for grouping | +| `description` | `str \| None` | `None` | Human-readable description | +| `tags` | `list[str]` | `[]` | Filtering tags | +| `resources` | `list[Resource]` | `[]` | Linked resource references | +| `validation_config` | `ValidationConfig \| None` | `None` | Validation command config | +| `context_config` | `ContextConfig` | `ContextConfig()` | Context indexing config | + +**Legacy fields preserved** (unchanged): +- `id: int | None` -- legacy integer ID +- `path: Path` -- project root path +- `settings: ProjectSettings` -- legacy settings +- `current_plan_id: int | None` -- active plan reference + +**Namespace validation:** +- Pattern: `^(local|[a-z][a-z0-9_]{0,49})$` +- Reserved names rejected: `system`, `internal`, `admin`, `root` + +**Computed property:** +- `is_remote` -- `True` only if the project has resources AND all are remote + +**Helper methods:** +- `namespaced_name` -- property returning `"{namespace}/{name}"` +- `parse_namespaced_name(str)` -- static method splitting `"namespace/name"` into a tuple +- `add_resource(resource)` -- returns a new `Project` with the resource appended +- `remove_resource(name)` -- returns a new `Project` without the named resource +- `get_resource(name)` -- returns `Resource | None` by name lookup + +--- + +## Files Changed + +| File | Action | Lines | +|------|--------|-------| +| `src/cleveragents/domain/models/core/resource.py` | Modified | 140 | +| `src/cleveragents/domain/models/core/project.py` | Modified | 345 | +| `src/cleveragents/domain/models/core/__init__.py` | Modified | 104 | + +## Files Created + +| File | Purpose | Lines | +|------|---------|-------| +| `features/resource_model.feature` | BDD scenarios for Resource + enums | 205 | +| `features/project_config_model.feature` | BDD scenarios for ValidationConfig + ContextConfig | 101 | +| `features/project_model.feature` | BDD scenarios for Project extensions | 125 | +| `features/steps/resource_model_steps.py` | Step definitions for resource tests | ~530 | +| `features/steps/project_config_model_steps.py` | Step definitions for config tests | ~250 | +| `features/steps/project_model_steps.py` | Step definitions for project tests | ~280 | + +## Exports + +All new types are exported via `src/cleveragents/domain/models/core/__init__.py`: + +```python +from cleveragents.domain.models.core import ( + Resource, + ResourceType, + SandboxStrategy, + ValidationConfig, + ContextConfig, +) +``` + +--- + +## Test Coverage + +| Feature File | Scenarios | Steps | Status | +|-------------|:---------:|:-----:|:------:| +| `resource_model.feature` | 47 | 88 | PASSING | +| `project_config_model.feature` | 19 | 65 | PASSING | +| `project_model.feature` | 24 | 58 | PASSING | +| **Total** | **90** | **211** | **ALL PASSING** | + +**Full regression suite:** 1702/1703 scenarios passing. The 1 failure (`plan_lifecycle_cli_coverage.feature:128`) is pre-existing and unrelated to B1. + +--- + +## Design Decisions + +1. **Backward compatibility** -- The existing `Project` fields (`id`, `path`, `settings`, `current_plan_id`) were preserved as-is. All new fields have defaults so existing code that constructs `Project(name=..., path=...)` continues to work. + +2. **`Resource` is frozen** -- Uses `frozen=True` config since resources are value objects. Mutation returns new instances. + +3. **`Project` is not frozen** -- Needs `validate_assignment=True` for the legacy `settings` field and because downstream code assigns to `current_plan_id`. + +4. **`project_id` is optional** -- Set to `str | None` with default `None` so legacy code that uses `id: int` isn't forced to provide a ULID. New code should use `project_id`. + +5. **Namespace defaults to `"local"`** -- Matches the specification's definition that single-machine projects use the `local` namespace. + +6. **Context ignore patterns merge, not replace** -- When users provide custom ignore patterns, defaults (`.git/`, `node_modules/`, etc.) are always preserved via the `field_validator`. + +7. **`add_resource` / `remove_resource` return new instances** -- Uses `model_copy(update=...)` pattern for immutable-style operations on the resource list. diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md new file mode 100644 index 000000000..1436bad56 --- /dev/null +++ b/CURRENT_TASK.md @@ -0,0 +1,114 @@ +# Current Task Tracker + +> **Active Workstream:** B1 - Project Data Model (Phase 1: Foundation) -- **COMPLETE** +> **Assignee:** Hamza (Python/RDF Expert, Infrastructure Lead) +> **Workflow:** BDD-First per `IMPLEMENTATION_PLAYBOOK.md` +> **Last Updated:** 2026-02-11 + +--- + +## Active Task + +**Stage B1 is complete.** Next up: B2 (Project CLI Commands) or B5 (Project Persistence). + +| Field | Value | +|-------|-------| +| **Next Task** | B2.1 or B5.1 | +| **Status** | READY TO START | +| **Depends On** | B1 (done) | + +--- + +## Completed Tasks + +| Task ID | Title | File(s) | Completed | +|---------|-------|---------|-----------| +| B1.3 | `ResourceType` enum | `resource.py` | 2026-02-10 | +| B1.3a | Create `resource.py` with proper imports | `resource.py` | 2026-02-10 | +| B1.3b | Define 6 enum values | `resource.py` | 2026-02-10 | +| B1.4 | `SandboxStrategy` enum | `resource.py` | 2026-02-10 | +| B1.4a | Define 6 enum values | `resource.py` | 2026-02-10 | +| B1.4b | Add `supports_rollback`, `is_copy_based` helpers | `resource.py` | 2026-02-10 | +| B1.2 | `Resource` Pydantic model | `resource.py` | 2026-02-11 | +| B1.2a | Create model structure (`frozen=True`) | `resource.py` | 2026-02-11 | +| B1.2b | All fields with defaults | `resource.py` | 2026-02-11 | +| B1.2c | Validators (ULID, name, location) | `resource.py` | 2026-02-11 | +| B1.2d | Properties (`supports_sandbox`, `can_write`, `get_sandbox_path`) | `resource.py` | 2026-02-11 | +| B1.5 | `ValidationConfig` model | `project.py` | 2026-02-11 | +| B1.5a | Create model in `project.py` | `project.py` | 2026-02-11 | +| B1.5b | All fields with defaults | `project.py` | 2026-02-11 | +| B1.5c | Helpers (`get_all_commands`, `has_any_validation`) | `project.py` | 2026-02-11 | +| B1.6 | `ContextConfig` model | `project.py` | 2026-02-11 | +| B1.6a | All fields with defaults | `project.py` | 2026-02-11 | +| B1.6b | Default ignore patterns via field_validator | `project.py` | 2026-02-11 | +| B1.1 | `Project` Pydantic model (extend) | `project.py` | 2026-02-11 | +| B1.1a | Import Resource, extend Project class | `project.py` | 2026-02-11 | +| B1.1b | Identity fields (project_id, namespace, description) | `project.py` | 2026-02-11 | +| B1.1c | Categorization (tags, resources, validation_config, context_config) | `project.py` | 2026-02-11 | +| B1.1d | Timestamp fields | `project.py` | 2026-02-11 | +| B1.1e | `is_remote` computed property | `project.py` | 2026-02-11 | +| B1.1f | Namespace validator (pattern + reserved names) | `project.py` | 2026-02-11 | +| B1.1g | `namespaced_name`, `add_resource`, `remove_resource`, `get_resource` | `project.py` | 2026-02-11 | + +### Tests Written + +| Feature File | Scenarios | Status | +|-------------|-----------|--------| +| `features/resource_model.feature` | 47 scenarios (ResourceType + SandboxStrategy + Resource) | PASSING | +| `features/project_config_model.feature` | 19 scenarios (ValidationConfig + ContextConfig) | PASSING | +| `features/project_model.feature` | 24 scenarios (Project extensions) | PASSING | +| **Total** | **90 scenarios** | **ALL PASSING** | + +### Step Definition Files + +| File | Lines | +|------|-------| +| `features/steps/resource_model_steps.py` | ~530 lines | +| `features/steps/project_config_model_steps.py` | ~250 lines | +| `features/steps/project_model_steps.py` | ~280 lines | + +--- + +## Production Code Changes + +| File | Changes | +|------|---------| +| `src/cleveragents/domain/models/core/resource.py` | Added `Resource` Pydantic model (B1.2) | +| `src/cleveragents/domain/models/core/project.py` | Added `ValidationConfig` (B1.5), `ContextConfig` (B1.6), extended `Project` (B1.1) | +| `src/cleveragents/domain/models/core/__init__.py` | Exported `Resource`, `ValidationConfig`, `ContextConfig` | + +--- + +## Regression Status + +- Full unit test suite: **107/108 features passing** (1702/1703 scenarios) +- The 1 failing scenario (`plan_lifecycle_cli_coverage.feature:128`) is a **pre-existing** failure unrelated to B1 changes +- No regressions introduced + +--- + +## BDD Workflow Checklist (per task) + +Reference: `IMPLEMENTATION_PLAYBOOK.md` Section 2 + +``` +1. [X] Understand task (read implementation_plan.md + specification.md) +2. [X] Write .feature file scenarios +3. [X] Write step definitions in _steps.py +4. [X] Run tests -- must FAIL (no implementation yet) +5. [X] Implement production code +6. [X] Run tests -- must PASS +7. [ ] Write Robot Framework integration test (if applicable) +8. [X] Full validation (nox unit_tests) +9. [X] Update tracking (HAMZA_PROGRESS.md, CURRENT_TASK.md) +``` + +--- + +## Notes + +- Legacy `Project` fields preserved (`id`, `path`, `settings`, `current_plan_id`) for backward compatibility +- `ProjectSettings` and `ProjectStats` untouched +- `Resource` model uses `frozen=True`; `Project` does not (needs `add_resource` etc.) +- `ContextConfig` merges user ignore patterns with defaults via `field_validator` +- Migration chain: `001_initial_schema` -> `4b518923afb2_add_debug_attempts` -> `c3d9b3d0cf3e_add_actors` diff --git a/HAMZA_PROGRESS.md b/HAMZA_PROGRESS.md new file mode 100644 index 000000000..5a84ec18b --- /dev/null +++ b/HAMZA_PROGRESS.md @@ -0,0 +1,213 @@ +# Hamza's Progress Tracker + +> **Role:** Python/RDF Expert, Infrastructure Lead (Workstream B) +> **Last Updated:** 2026-02-11 +> **Overall Progress:** 24 / 238 tasks (10%) + +--- + +## Quick Status Dashboard + +| Stage | Description | Tasks | Done | Status | Blocked By | +|-------|-------------|-------|------|--------|------------| +| **B1** | Project Data Model | 24 | 24 | **COMPLETE** | None | +| **B2** | Project CLI Commands | 32 | 0 | NOT STARTED | B1 | +| **B3.3-B3.4** | Sandbox Implementations | 14 | 0 | NOT STARTED | B1, Luis (B3.1-B3.2) | +| **B4** | Resource Integration | 27 | 0 | NOT STARTED | B3 complete | +| **B5** | Project Persistence | 18 | 0 | NOT STARTED | B1 | +| **SEC5** | Secrets Management | 3 | 0 | NOT STARTED | None | +| **SEC7** | Audit Logging | 3 | 0 | NOT STARTED | DB infra | +| **SESS1** | Session Management | 5 | 0 | NOT STARTED | DB infra | +| **SESS2** | Memory Persistence | 4 | 0 | NOT STARTED | SESS1 | +| **CLI0** | Core System Commands | 3 | 0 | NOT STARTED | Services | +| **CLI1** | Plan Interaction CLI | 4 | 0 | NOT STARTED | Plan services | +| **CLI2** | Configuration Commands | 4 | 0 | NOT STARTED | Config infra | +| **CLI3** | Context Commands | 4 | 0 | NOT STARTED | Context service | +| **CONC3** | Garbage Collection | 4 | 0 | NOT STARTED | Sandbox + checkpoints | +| **CTX1** | Repository Indexing | 3 | 0 | NOT STARTED | Project model | +| **CTX2** | Embedding Index | 3 | 0 | NOT STARTED | CTX1 | +| **D1** | Decision Data Model | 14 | 0 | NOT STARTED | After M3 (Day 14) | +| **D2** | Decision Recording | 14 | 0 | NOT STARTED | D1 | +| **D3** | Decision CLI | 12 | 0 | NOT STARTED | D2 | +| **D4.4-D4.5** | Correction CLI | 5 | 0 | NOT STARTED | Jeff (D4.1-D4.3) | +| **D5** | Decision Persistence | 18 | 0 | NOT STARTED | D1 | +| **E5** | Multi-Project Plans | 8 | 0 | NOT STARTED | E1-E4 (Luis/Jeff) | +| **G4** | Context Tiers | 5 | 0 | NOT STARTED | CTX1/CTX2 | +| **G5** | Cost & Risk Estimation | 4 | 0 | NOT STARTED | Actor framework | +| **C3.6e** | Git Operation Skills | 4 | 0 | NOT STARTED | Jeff (C3.1-C3.5) | +| **F4** | Remote Project Support | 2 | 0 | DEFERRED | F1-F3 (Luis) | + +--- + +## Priority Execution Order + +### Phase 1: Foundation (Days 1-2) -- NO BLOCKERS + +#### Stage B1: Project Data Model +File targets: `src/cleveragents/domain/models/core/resource.py`, `src/cleveragents/domain/models/core/project.py` + +- [X] **B1.3** - Define `ResourceType` enum *(2026-02-10)* + - [X] B1.3a - Create `resource.py` with proper imports + - [X] B1.3b - Define values: GIT_REPOSITORY, FILESYSTEM, DATABASE, API_ENDPOINT, DOCUMENT_CORPUS, CLOUD_INFRASTRUCTURE +- [X] **B1.4** - Define `SandboxStrategy` enum *(2026-02-10)* + - [X] B1.4a - Define values: GIT_WORKTREE, COPY_ON_WRITE, OVERLAY, TRANSACTION_ROLLBACK, VERSIONING, NONE + - [X] B1.4b - Add helpers: `supports_rollback`, `is_copy_based` +- [X] **B1.2** - Define `Resource` Pydantic model *(2026-02-11)* + - [X] B1.2a - Create model structure (`frozen=True`) + - [X] B1.2b - Fields: resource_id, name, type, location, is_remote, sandbox_strategy, read_only, metadata, created_at + - [X] B1.2c - Validators: ULID, name (alphanumeric + lowercase), min_length + - [X] B1.2d - Properties: `supports_sandbox`, `can_write`, `get_sandbox_path` +- [X] **B1.5** - Define `ValidationConfig` model *(2026-02-11)* + - [X] B1.5a - Create model in `project.py` + - [X] B1.5b - Fields: test_command, lint_command, type_check_command, build_command, custom_commands, timeout_seconds, fail_on_lint_error + - [X] B1.5c - Helpers: `get_all_commands`, `has_any_validation` +- [X] **B1.6** - Define `ContextConfig` model *(2026-02-11)* + - [X] B1.6a - Fields: ignore_patterns, include_patterns, max_file_size, max_files, indexing_strategy, chunking_policy, chunk_size + - [X] B1.6b - Add default ignore patterns via field_validator +- [X] **B1.1** - Define `Project` Pydantic model *(2026-02-11)* + - [X] B1.1a - Import Resource, create Project class + - [X] B1.1b - Identity fields: project_id, name, namespace, description + - [X] B1.1c - Categorization: tags, resources, validation_config, context_config + - [X] B1.1d - Timestamp fields + - [X] B1.1e - `is_remote` computed property + - [X] B1.1f - Namespace validator (pattern + reserved names) + - [X] B1.1g - `namespaced_name` property, `add_resource`, `remove_resource`, `get_resource` + +### Phase 2: CLI Layer (Days 3-4) -- Depends on B1 + +#### Stage B2: Project CLI Commands +File targets: `src/cleveragents/cli/commands/project.py`, `src/cleveragents/application/services/project_service.py` + +- [ ] **B2.1** - Create CLI scaffold + ProjectService + - [ ] B2.1a - Create `project.py` with imports and Typer group + - [ ] B2.1b - Create `ProjectService` in `project_service.py` +- [ ] **B2.2** - `project create` command + - [ ] B2.2a-e - Command signature, namespace parsing, creation, output, service method +- [ ] **B2.3** - `project add-resource` command + - [ ] B2.3a-f - Signature, type validation, location validation, metadata parsing, resource creation, service method +- [ ] **B2.4** - `project remove-resource` command + - [ ] B2.4a-c - Signature, removal logic, service method +- [ ] **B2.5** - `project list` command + - [ ] B2.5a-e - Signature, query/filtering, table output, JSON output, service method +- [ ] **B2.6** - `project show` command + - [ ] B2.6a-d - Signature, fetch + rich display, JSON output, service method +- [ ] **B2.7** - `project set-validation` command + - [ ] B2.7a-c - Signature, config update, service method +- [ ] **B2.8** - `project delete` command + - [ ] B2.8a-d - Signature, deletion checks, deletion, service method +- [ ] **B2.9** - Register commands in `main.py` + - [ ] B2.9a - Import and register + - [ ] B2.9b - DI wiring for ProjectService + +### Phase 3: Sandbox Implementations (Days 3-5) -- Depends on B1 + Luis (B3.1-B3.2) + +#### Stage B3.3-B3.4: Sandbox Implementations +File targets: `src/cleveragents/infrastructure/sandbox/git_worktree.py`, `src/cleveragents/infrastructure/sandbox/filesystem.py` + +- [ ] **B3.3** - `GitWorktreeSandbox` + - [ ] B3.3a-h - Constructor, create(), _run_git(), get_path(), commit(), rollback(), cleanup(), error handling +- [ ] **B3.4** - `FilesystemSandbox` + - [ ] B3.4a-f - Constructor, create() with copytree, get_path(), commit() with diff, rollback(), cleanup() + +### Phase 4: Resource Service + Persistence (Days 6-8) + +#### Stage B4: Resource Integration (depends on B3) +File targets: `src/cleveragents/application/services/resource_service.py`, `src/cleveragents/domain/models/core/resource_access.py` + +- [ ] **B4.1** - Resource access types (AccessMode, ResourceAccess, ResourceAccessTracker) +- [ ] **B4.2** - ResourceService scaffold + config +- [ ] **B4.3** - `access_resource()` method (read/write/upgrade) +- [ ] **B4.4** - Lazy sandboxing pattern +- [ ] **B4.5** - Commit and rollback methods +- [ ] **B4.6** - Cleanup hooks (plan completion, failure, exit, startup) +- [ ] **B4.7** - PlanLifecycleService integration + DI wiring + +#### Stage B5: Project Persistence (depends on B1, parallel with B4) +File targets: `models.py`, `repositories.py`, Alembic migrations + +- [ ] **B5.1** - Alembic migration: `projects` table +- [ ] **B5.2** - Alembic migration: `resources` table (FK to projects) +- [ ] **B5.3** - `ProjectModel` SQLAlchemy model + domain conversion +- [ ] **B5.4** - `ResourceModel` SQLAlchemy model + domain conversion +- [ ] **B5.5** - `ProjectRepository` (CRUD: create, get_by_id, get_by_name, get_with_resources, list_all, update, delete) +- [ ] **B5.6** - `ResourceRepository` (CRUD: create, get_by_project, get_by_name, delete) + +### Phase 5: Security & Sessions (Days 6-10) + +- [ ] **SEC5.1-5.3** - Secrets masking, env var handling, prevent secrets in code +- [ ] **SEC7.1-7.3** - Apply audit logging, audit_log migration, audit list CLI +- [ ] **SESS1.1-1.5** - Session model, service, migration, persistence, CLI +- [ ] **SESS2.1-2.4** - Memory service updates, conversation_history migration, config, warning + +### Phase 6: CLI Commands (Days 10-14) + +- [ ] **CLI0.1-0.3** - version, info, diagnostics commands +- [ ] **CLI1.1-1.4** - plan prompt, plan diff, plan diff --correction, plan artifacts +- [ ] **CLI2.1-2.4** - config set/get/list, providers list +- [ ] **CLI3.1-3.4** - project context set/show, actor context set/show +- [ ] **CONC3.1-3.4** - Sandbox GC, checkpoint cleanup, session cleanup, auto-cleanup +- [ ] **CTX1.1-1.3** - IndexingService, file tree, language detection +- [ ] **CTX2.1-2.3** - VectorStore integration, semantic search, optional embeddings + +### Phase 7: Decision Tree (Days 15-21) -- After M3 merge + +- [ ] **D1.1-1.4** - DecisionType enum, ContextSnapshot, Decision model, helpers +- [ ] **D2.1-2.6** - DecisionService, record_decision, tree queries, snapshots, strategy integration +- [ ] **D3.1-3.4** - plan tree CLI, plan explain CLI, JSON output, --guidance-file +- [ ] **D4.4-4.5** - plan correct revert CLI, plan correct append CLI (needs Jeff D4.1-D4.3) +- [ ] **D5.1-5.6** - Alembic migrations (decisions, dependencies, corrections, snapshots) + models + repos + +### Phase 8: Advanced Features (Days 22-35) + +- [ ] **E5.1-5.4** - Multi-project plan support (needs E1-E4) +- [ ] **G4.1-4.5** - Context tiers: hot/warm/cold + actor views + promotion/demotion +- [ ] **G5.1-5.4** - Cost estimation actor, token/cost calc, risk assessment, display +- [ ] **C3.6e** - Git operation skills (needs Jeff C3.1-C3.5) +- [ ] **F4.1-4.2** - Remote project support (DEFERRED, needs Luis F1-F3) + +--- + +## Merge Point Checkpoints + +| Merge | Day | Hamza's Verification Task | +|-------|-----|---------------------------| +| **M1** | 8 | M1.7 - Verify Plan-Actor binding with real actors | +| **M3** | 14 | M3.4 - Verify sandbox commit applies changes to original | +| **M4** | 21 | M4.1 - Decision recording captures context; M4.3 - Tree visualization works | +| **M6** | 30 | M6.4 - Deep subplan hierarchies; M6.7 - Cold tier queries | + +--- + +## Deliverable File Index + +| Category | File Path | +|----------|-----------| +| Domain Models | `src/cleveragents/domain/models/core/resource.py` | +| | `src/cleveragents/domain/models/core/project.py` | +| | `src/cleveragents/domain/models/core/resource_access.py` | +| | `src/cleveragents/domain/models/core/decision.py` | +| | `src/cleveragents/domain/models/core/session.py` | +| CLI Commands | `src/cleveragents/cli/commands/project.py` | +| | `src/cleveragents/cli/commands/plan.py` (extend) | +| | `src/cleveragents/cli/main.py` (register) | +| Services | `src/cleveragents/application/services/project_service.py` | +| | `src/cleveragents/application/services/resource_service.py` | +| | `src/cleveragents/application/services/decision_service.py` | +| | `src/cleveragents/application/services/session_service.py` | +| | `src/cleveragents/application/services/indexing_service.py` | +| Sandbox | `src/cleveragents/infrastructure/sandbox/git_worktree.py` | +| | `src/cleveragents/infrastructure/sandbox/filesystem.py` | +| Database | `src/cleveragents/infrastructure/database/models.py` (extend) | +| | `src/cleveragents/infrastructure/database/repositories.py` (extend) | +| Config | `src/cleveragents/config/settings.py` (extend) | +| Migrations | projects, resources, decisions, decision_dependencies, correction_attempts, context_snapshots, audit_log, sessions, conversation_history | +| Tests (Behave) | `features/resource_model.feature`, `features/project_model.feature`, `features/project_cli.feature`, etc. | +| Tests (Robot) | `robot/project_integration.robot`, `robot/sandbox_integration.robot`, etc. | + +--- + +## Notes / Blockers Log + +| Date | Note | +|------|------| +| 2026-02-10 | Initial tracker created. All 238 tasks pending. Starting with B1. | diff --git a/IMPLEMENTATION_PLAYBOOK.md b/IMPLEMENTATION_PLAYBOOK.md new file mode 100644 index 000000000..35b584a85 --- /dev/null +++ b/IMPLEMENTATION_PLAYBOOK.md @@ -0,0 +1,718 @@ +# CleverAgents Implementation Playbook + +> Generic guide for implementing any feature, fix, or task in the CleverAgents codebase. +> Use this as the base workflow every time. No exceptions. + +--- + +## Table of Contents + +1. [Development Approach: BDD-First (Not Pure TDD)](#1-development-approach-bdd-first) +2. [The Standard Workflow](#2-the-standard-workflow) +3. [Architecture Rules](#3-architecture-rules) +4. [File Organization](#4-file-organization) +5. [Coding Standards](#5-coding-standards) +6. [Testing Guide](#6-testing-guide) +7. [Database Changes Guide](#7-database-changes-guide) +8. [CLI Commands Guide](#8-cli-commands-guide) +9. [Commands Reference](#9-commands-reference) +10. [Checklist Templates](#10-checklist-templates) + +--- + +## 1. Development Approach: BDD-First + +This project uses **BDD-First** (Behavior-Driven Development): + +### The Core Rule + +> **Write the `.feature` file FIRST. Then the step definitions. Then the implementation. Never the other way around.** + +--- + +## 2. The Standard Workflow + +Every task follows this exact 8-step sequence. No skipping steps. + +### Step 1: Understand the Task + +- Read the task description in `implementation_plan.md` +- Read the specification in `docs/specification.md` if the domain is unclear +- Identify the deliverable files (models, services, CLI, tests) +- Identify dependencies (what must exist before you start) + +### Step 2: Write the Behave Feature File + +Create `features/.feature`: + +```gherkin +Feature: Resource Type Management + As a developer + I want to define resource types for projects + So that the system can handle different resource kinds appropriately + + Scenario: Create a valid git repository resource type + Given I have the ResourceType enum imported + When I access ResourceType.GIT_REPOSITORY + Then the value should be "git_repository" + + Scenario: Reject invalid resource type + When I try to create a resource with type "invalid_type" + Then a validation error should be raised + And the error should mention "resource type" + + Scenario: SandboxStrategy supports rollback check + Given I have a GIT_WORKTREE sandbox strategy + When I check if it supports rollback + Then the result should be true + + Scenario: SandboxStrategy copy-based check + Given I have a COPY_ON_WRITE sandbox strategy + When I check if it is copy based + Then the result should be true +``` + +**Rules:** +- One `.feature` per logical domain concept +- Use `Background:` for shared setup +- Cover happy path, validation errors, edge cases +- Name: `features/.feature` + +### Step 3: Write Step Definitions + +Create `features/steps/_steps.py`: + +```python +"""Step definitions for Resource Type tests.""" + +from behave import given, then, when +from behave.runner import Context + + +@given("I have the ResourceType enum imported") +def step_import_resource_type(context: Context) -> None: + """Import ResourceType enum.""" + from cleveragents.domain.models.core.resource import ResourceType + context.resource_type_cls = ResourceType + + +@when("I access ResourceType.GIT_REPOSITORY") +def step_access_git_repo(context: Context) -> None: + """Access the GIT_REPOSITORY enum value.""" + context.result = context.resource_type_cls.GIT_REPOSITORY + + +@then('the value should be "{expected}"') +def step_check_value(context: Context, expected: str) -> None: + """Verify enum value.""" + assert context.result.value == expected, ( + f"Expected '{expected}', got '{context.result.value}'" + ) +``` + +**Rules:** +- Step file name matches feature file: `foo.feature` -> `foo_steps.py` +- Steps private to one feature MUST live in that feature's step file +- Use `context` to pass state between steps +- Always type-annotate: `context: Context`, return `-> None` +- Always add a docstring to every step function +- Use `context.error` pattern for testing validation errors +- NEVER add placeholder steps -- implement fully or don't add + +### Step 4: Run Tests (They Should FAIL) + +```bash +nox -e unit_tests -- features/.feature +``` + +This MUST fail because the implementation doesn't exist yet. If it passes, your tests are wrong. + +### Step 5: Implement the Production Code + +Follow the architecture layers (see Section 3). Implement in this order: + +1. **Domain models** (`src/cleveragents/domain/models/core/`) +2. **Domain interfaces** (protocols, repository interfaces) +3. **Infrastructure** (DB models, repositories, migrations) +4. **Application services** (`src/cleveragents/application/services/`) +5. **DI wiring** (`src/cleveragents/application/container.py`) +6. **CLI commands** (`src/cleveragents/cli/commands/`) + +### Step 6: Run Tests (They Should PASS) + +```bash +# Run your specific feature +nox -e unit_tests -- features/.feature + +# Run type checking +nox -e typecheck + +# Run linting +nox -e lint +``` + +Fix any failures. Iterate between Step 5 and Step 6 until green. + +### Step 7: Write Robot Framework Integration Test + +Create `robot/.robot`: + +```robot +*** Settings *** +Documentation Integration tests for Resource types +Library OperatingSystem +Library Process +Resource common.resource + +*** Test Cases *** +Create Project With Git Resource + [Documentation] Verify project creation with git resource end-to-end + Setup Test Environment + ${result}= Run Python Script + ... from cleveragents.domain.models.core.resource import Resource, ResourceType + ... r = Resource(resource_id="01HXYZ...", name="repo", type=ResourceType.GIT_REPOSITORY, location="/tmp/repo") + ... print(r.name) + Should Be Equal ${result} repo + Cleanup Test Environment +``` + +Run: `nox -e integration_tests -- robot/.robot` + +### Step 8: Full Validation + +```bash +# Run everything +nox + +# This executes: lint, format check, typecheck, unit tests, integration tests, docs build, coverage check +``` + +ALL must pass. Coverage must stay above 85%. + +### Step 9: Update Tracking + +- Check off the task in `implementation_plan.md` (`[ ]` -> `[X]`) +- Update `HAMZA_PROGRESS.md` with completion status +- Commit with conventional message: `feat(project): add ResourceType enum (B1.3)` + +--- + +## 3. Architecture Rules + +### Layer Diagram + +``` +CLI (Typer) + | + v +Application Services (business logic orchestration) + | + v +Domain Models (Pydantic) + Domain Interfaces (Protocols) + | + v +Infrastructure (SQLAlchemy, Alembic, Sandbox, Providers) + | + v +DI Container (dependency-injector) wires everything together +``` + +### Dependency Rules + +- **CLI** depends on **Application Services** only (never infrastructure directly) +- **Application Services** depend on **Domain Models** + **Domain Interfaces** +- **Infrastructure** implements **Domain Interfaces** +- **Domain Models** depend on NOTHING (pure data + validation) +- **DI Container** wires interfaces to implementations + +### Design Patterns in Use + +| Pattern | Where | Example | +|---------|-------|---------| +| Repository | Data access | `ProjectRepository`, `PlanRepository` | +| Unit of Work | Transactions | `UnitOfWork` + `UnitOfWorkContext` | +| Dependency Injection | Wiring | `Container` with providers.Factory/Singleton | +| Strategy | Sandboxing | `GitWorktreeSandbox`, `FilesystemSandbox` | +| State Machine | Plan lifecycle | `PlanPhase` transitions with `can_transition()` | +| Factory | Object creation | `providers.Factory(ProjectService, ...)` | +| Protocol | Interfaces | `AIProviderInterface`, `SandboxProtocol` | + +--- + +## 4. File Organization + +### Where Things Go + +| What | Where | Naming | +|------|-------|--------| +| Domain model | `src/cleveragents/domain/models/core/.py` | snake_case, singular noun | +| Domain enum | Same file as the model it belongs to | PascalCase | +| Repository interface | `src/cleveragents/domain/repositories/` | `_repository.py` | +| SQLAlchemy model | `src/cleveragents/infrastructure/database/models.py` | `Model` | +| Repository impl | `src/cleveragents/infrastructure/database/repositories.py` | `Repository` | +| Alembic migration | `alembic/versions/` | Auto-generated name | +| Application service | `src/cleveragents/application/services/_service.py` | `Service` | +| CLI command group | `src/cleveragents/cli/commands/.py` | Typer app | +| Behave feature | `features/.feature` | snake_case | +| Behave steps | `features/steps/_steps.py` | matches feature | +| Mocks | `features/mocks/` | `mock_.py` | +| Robot test | `robot/.robot` | snake_case | +| Config/Settings | `src/cleveragents/config/settings.py` | Extend existing | + +### File Size Limit + +**500 lines max per file.** If approaching this, split into focused submodules. + +### Exports + +Every `__init__.py` in the models package MUST export all public types via `__all__`. + +--- + +## 5. Coding Standards + +### Type Annotations + +```python +# REQUIRED on all functions, methods, and variables where not obvious +def create_project(self, name: str, namespace: str | None = None) -> Project: + ... + +# Use | for unions (Python 3.13) +value: str | None = None +items: list[str] | tuple[str, ...] = [] + +# Use TYPE_CHECKING guard for import-only types +from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from cleveragents.domain.models.core.project import Project +``` + +### Argument Validation (MANDATORY for all public/protected methods) + +```python +def process(self, data: list[str], threshold: int) -> Result: + """Process data with threshold. + + Args: + data: Non-empty list of strings to process. + threshold: Value between 0 and 100. + + Returns: + Processing result. + + Raises: + ValueError: If data is empty or threshold out of range. + TypeError: If data contains non-string items. + """ + if not data: + raise ValueError("data cannot be empty") + if not all(isinstance(item, str) for item in data): + raise TypeError("data must contain only strings") + if threshold < 0 or threshold > 100: + raise ValueError(f"threshold must be between 0 and 100, got {threshold}") + # ... actual logic +``` + +### Error Handling + +```python +# GOOD: Catch specific, add context, re-raise or handle +try: + result = self.repository.get_by_id(project_id) +except DatabaseError as e: + raise ProjectNotFoundError(f"Failed to fetch project {project_id}") from e + +# BAD: Never do these +except: # bare except +except Exception: # too broad without re-raise +except Exception as e: # swallowing the error + return None # silent failure +``` + +### Pydantic Model Pattern + +```python +"""Resource domain model. + +Implements ADR-004 (Data Validation) and specification section X.Y. +""" + +from datetime import datetime +from enum import Enum +from pydantic import BaseModel, ConfigDict, Field, field_validator + +ULID_PATTERN = r"^[0-9A-HJKMNP-TV-Z]{26}$" + + +class ResourceType(str, Enum): + """Types of resources a project can reference.""" + GIT_REPOSITORY = "git_repository" + FILESYSTEM = "filesystem" + + +class Resource(BaseModel): + """A project resource reference. + + Resources represent external data sources or repositories + that a project operates on. + """ + resource_id: str = Field(..., description="Unique ULID", pattern=ULID_PATTERN) + name: str = Field(..., min_length=1, max_length=255, description="Human-readable name") + type: ResourceType = Field(..., description="Resource classification") + location: str = Field(..., min_length=1, description="Path or URI") + created_at: datetime = Field(default_factory=datetime.now) + + @field_validator("name") + @classmethod + def validate_name(cls: type["Resource"], v: str) -> str: + """Enforce naming rules.""" + if not v.replace("-", "").replace("_", "").isalnum(): + raise ValueError("Name must be alphanumeric with hyphens/underscores") + return v.lower() + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + use_enum_values=False, + frozen=True, + ) +``` + +### Import Rules + +- ALL imports at top of file (no inline imports) +- Exception: `if TYPE_CHECKING:` guard +- Use absolute imports: `from cleveragents.domain.models.core.resource import Resource` +- Order: stdlib -> third-party -> local (enforced by ruff `I` rule) + +--- + +## 6. Testing Guide + +### Behave (Unit/Behavioral Tests) + +| Aspect | Rule | +|--------|------| +| Location | `features/*.feature` + `features/steps/*_steps.py` | +| Runner | `nox -e unit_tests` (NEVER run behave directly) | +| Mock placement | `features/mocks/` ONLY | +| Coverage | Must stay above 85% (`nox -e coverage_report`) | +| Parallelism | Tests run in parallel via behave-parallel | +| Tags | `@discovery` for Phase 0 tests (excluded by default) | +| Environment | `features/environment.py` handles setup/teardown | + +#### Error Testing Pattern + +```gherkin +Scenario: Reject resource with empty name + When I try to create a resource with name "" + Then a validation error should be raised + And the error should mention "name" +``` + +```python +@when('I try to create a resource with name "{name}"') +def step_try_create_resource(context: Context, name: str) -> None: + """Attempt resource creation that may fail.""" + context.error = None + try: + context.resource = Resource( + resource_id="01HXYZABC123DEF456GHJ789KL", + name=name, + type=ResourceType.GIT_REPOSITORY, + location="/tmp/repo", + ) + except (ValidationError, ValueError) as e: + context.error = e + + +@then("a validation error should be raised") +def step_check_error_raised(context: Context) -> None: + """Verify an error was captured.""" + assert context.error is not None, "Expected a validation error but none was raised" + + +@then('the error should mention "{substring}"') +def step_check_error_message(context: Context, substring: str) -> None: + """Verify error message contains expected text.""" + assert substring.lower() in str(context.error).lower(), ( + f"Expected error to mention '{substring}', got: {context.error}" + ) +``` + +### Robot Framework (Integration Tests) + +| Aspect | Rule | +|--------|------| +| Location | `robot/*.robot` | +| Runner | `nox -e integration_tests` (NEVER run robot directly) | +| Shared resources | `robot/common.resource` | +| Python helpers | `robot/*.py` in same directory | +| Parallelism | Via pabot | +| Tags | `@slow` excluded from default runs | + +#### Integration Test Pattern + +```robot +*** Settings *** +Documentation Integration tests for Project persistence +Library OperatingSystem +Library Process +Resource common.resource + +*** Test Cases *** +Create And Retrieve Project From Database + [Documentation] End-to-end project CRUD via database + Setup Test Environment + ${project_id}= Create Test Project my-project default + ${retrieved}= Get Project By Id ${project_id} + Should Be Equal ${retrieved.name} my-project + Cleanup Test Environment +``` + +### What to Test + +| Layer | Test Type | What to Cover | +|-------|-----------|---------------| +| Domain Model | Behave | Creation, validation, computed properties, state transitions, edge cases | +| Service | Behave | Business logic, orchestration, error handling | +| Repository | Robot | CRUD operations, queries, transactions, UoW | +| CLI | Robot | Command execution, output format, error messages | +| Sandbox | Robot | File operations, git operations, cleanup | + +--- + +## 7. Database Changes Guide + +### Adding a New Table + +1. **Define the SQLAlchemy model** in `src/cleveragents/infrastructure/database/models.py`: + +```python +class ProjectModel(Base): + __tablename__ = "projects" + id = Column(Integer, primary_key=True, autoincrement=True) + project_id = Column(String(26), nullable=False, unique=True) # ULID + name = Column(String(255), nullable=False, unique=True) + namespace = Column(String(100), nullable=False, default="default") + settings_json = Column(JSON, nullable=False, default=dict) + created_at = Column(DateTime, nullable=False, default=datetime.now) +``` + +2. **Create Alembic migration**: + +```python +"""Add projects and resources tables. + +Revision ID: +Revises: c3d9b3d0cf3e # MUST chain from latest migration +Create Date: 2026-02-10 +""" +from collections.abc import Sequence +import sqlalchemy as sa +from alembic import op + +revision: str = "" +down_revision: str | Sequence[str] | None = "c3d9b3d0cf3e" + +def upgrade() -> None: + op.create_table("projects", + sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), + sa.Column("project_id", sa.String(26), nullable=False), + sa.Column("name", sa.String(255), nullable=False), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("project_id"), + sa.UniqueConstraint("name"), + ) + +def downgrade() -> None: + op.drop_table("projects") +``` + +3. **Implement repository** in `src/cleveragents/infrastructure/database/repositories.py` + +4. **Register in UoW** as lazy property on `UnitOfWorkContext` + +5. **Wire in DI container** if needed + +Current migration chain: `001_initial_schema` -> `4b518923afb2_add_debug_attempts` -> `c3d9b3d0cf3e_add_actors` + +--- + +## 8. CLI Commands Guide + +### Adding a New Command Group + +1. **Create command file** `src/cleveragents/cli/commands/.py`: + +```python +"""Project management CLI commands.""" + +from typing import Annotated, Optional + +import typer +from rich.console import Console +from rich.table import Table + +from cleveragents.application.container import get_container + +app = typer.Typer(help="Manage projects") +console = Console() + + +@app.command() +def create( + name: Annotated[str, typer.Argument(help="Project name")], + namespace: Annotated[Optional[str], typer.Option("--namespace", "-n", help="Project namespace")] = None, + description: Annotated[Optional[str], typer.Option("--description", "-d", help="Description")] = None, +) -> None: + """Create a new project.""" + container = get_container() + service = container.project_service() + + project = service.create_project(name=name, namespace=namespace or "default", description=description) + + console.print(f"[green]Created project:[/green] {project.namespaced_name}") +``` + +2. **Register in main.py** (`src/cleveragents/cli/main.py`): + +```python +from cleveragents.cli.commands.project import app as project_app +app.add_typer(project_app, name="project") +``` + +--- + +## 9. Commands Reference + +### Development Commands (Always Use nox) + +```bash +# Full validation suite (lint + format + typecheck + tests + docs + build + coverage) +nox + +# Individual sessions +nox -e lint # Ruff linter +nox -e format # Ruff formatter +nox -e typecheck # Pyright strict mode +nox -e unit_tests # Behave BDD tests (parallel) +nox -e integration_tests # Robot Framework tests (parallel) +nox -e coverage_report # Behave with coverage (>85% required) +nox -e docs # Build MkDocs +nox -e build # Build wheel + +# Run a single feature file +nox -e unit_tests -- features/resource_model.feature + +# Run a single robot file +nox -e integration_tests -- robot/project_integration.robot +``` + +### NEVER Run Directly + +```bash +# WRONG - never do this +behave features/foo.feature +robot robot/foo.robot +pytest +python -m pytest +ruff check . +pyright +``` + +### Git Workflow + +```bash +# Commit convention +git commit -m "feat(project): add ResourceType enum (B1.3)" +git commit -m "test(project): add resource model BDD scenarios" +git commit -m "fix(sandbox): handle missing worktree directory" + +# Types: feat, fix, chore, docs, test, refactor, perf, style +# Scope: the domain area (project, plan, sandbox, decision, cli, etc.) +``` + +--- + +## 10. Checklist Templates + +### New Domain Model Checklist + +``` +- [ ] Write `.feature` file with all scenarios +- [ ] Write step definitions in `_steps.py` +- [ ] Run `nox -e unit_tests -- features/.feature` (should FAIL) +- [ ] Implement model in `src/cleveragents/domain/models/core/.py` +- [ ] Add exports to `__init__.py` +- [ ] Run `nox -e unit_tests -- features/.feature` (should PASS) +- [ ] Run `nox -e typecheck` (should PASS) +- [ ] Run `nox -e lint` (should PASS) +- [ ] Update `implementation_plan.md` checkboxes +- [ ] Update `HAMZA_PROGRESS.md` +- [ ] Commit: `feat(): ()` +``` + +### New Database Table Checklist + +``` +- [ ] Write Robot integration test in `robot/.robot` +- [ ] Add SQLAlchemy model to `models.py` +- [ ] Create Alembic migration (chain from latest revision) +- [ ] Implement repository in `repositories.py` +- [ ] Add lazy property to `UnitOfWorkContext` +- [ ] Wire in DI container if needed +- [ ] Run `nox -e integration_tests -- robot/.robot` (should PASS) +- [ ] Run full `nox` suite +- [ ] Update tracking docs +- [ ] Commit: `feat(db): add persistence ()` +``` + +### New CLI Command Checklist + +``` +- [ ] Write Behave feature for command behavior +- [ ] Write Robot test for end-to-end command execution +- [ ] Create command file in `cli/commands/` +- [ ] Create or extend application service +- [ ] Wire service in DI container +- [ ] Register command in `cli/main.py` +- [ ] Run `nox` full suite +- [ ] Update tracking docs +- [ ] Commit: `feat(cli): add command ()` +``` + +### Bug Fix Checklist + +``` +- [ ] Write a Behave scenario that reproduces the bug (should FAIL) +- [ ] Identify root cause +- [ ] Implement fix +- [ ] Run failing scenario (should PASS) +- [ ] Run full `nox` suite (no regressions) +- [ ] Commit: `fix(): ()` +``` + +--- + +## Quick Decision Guide + +| Question | Answer | +|----------|--------| +| Where do I put a new model? | `src/cleveragents/domain/models/core/` | +| Where do I put tests? | `features/` (Behave) and `robot/` (Robot Framework) | +| Where do mocks go? | `features/mocks/` ONLY | +| How do I run tests? | `nox -e unit_tests` or `nox -e integration_tests` | +| What Python version? | 3.13 only | +| How do I check types? | `nox -e typecheck` | +| How do I format code? | `nox -e format` | +| What's the coverage target? | 85% minimum | +| Can I use pytest? | NO. Behave for unit, Robot for integration. | +| Can I skip type annotations? | NO. Everything must be typed. | +| Can I add `# type: ignore`? | NO. Fix the type issue instead. | +| Can I put test code in `src/`? | NO. Never. | +| File size limit? | 500 lines max | +| Commit message format? | `(): ` | diff --git a/implementation_plan.md b/implementation_plan.md index fcc6918e9..422cba2b5 100644 --- a/implementation_plan.md +++ b/implementation_plan.md @@ -1,6 +1,7 @@ # CleverAgents Implementation Plan ## **CRITICAL**: Execute These Rules Without Exception + - **Strictly adhere to guidelines in `./CONTRIBUTING.md`**: All rules and guidelines outlined in this file must be strictly followed at all times. - **Python implementation scope only**: Every action described here pertains to building an idiomatic Python codebase that implements the CleverAgents architecture. - **NO BACKWARDS COMPATIBILITY**: CleverAgents is a NEW standalone project. Do NOT maintain any backwards compatibility. No migration guides, no compatibility shims, no support for old configurations or data. @@ -8,7 +9,7 @@ - **Single documentation surface**: Do not create auxiliary notes elsewhere unless explicitly required. All architectural updates, troubleshooting outcomes, and contextual knowledge must flow back into this markdown file. - **Sequential discipline**: Always begin with the first unchecked item in the checklist. Do not progress until that item, its documentation update, and its testing sub-items (including any spawned remediation tasks) are fully resolved. - **USE MODERN PYTHON TOOLING**: This is a cutting-edge Python project that must use modern build tools and workflows. NO Makefiles, NO legacy approaches, NO helper scripts. Use Hatch exclusively for project management, nox for task automation, pyproject.toml for all configuration. Commands should be Python-native (e.g., `hatch env create`, `nox -s test`) not shell scripts or make targets. All tooling must be from the current Python ecosystem (2024+). When current tooling (such as "Behave" and "Robot Framework") can be used to solve a problem, use them rather than adding new tooling, keep it simple. NO wrapper scripts - use tools directly as designed. -- **Unit + integration + asv testing mandate**: For every coding task, author or update must include asv (airspeed velocity) performance, unit and integration tests, run them, and achieve passing results before marking the task complete. Testing subtasks are non-optional. +- **Unit + integration testing mandate**: For every coding task, author or update both unit and integration tests, run them, and achieve passing results before marking the task complete. Testing subtasks are non-optional. - **Behavior-driven testing stack**: Use Behave feature suites under `features/` for unit-level and scenario tests and Robot Framework suites under `robot/` for integration and end-to-end coverage. Keep both synchronized with the code under test and document all updates in this plan. - **Do not use pytest style unit tests**: Under no circumstances should you write pytest styled unit tests, all unit tests should be Behave based (as noted in the last bullet point), which follows the Cucumber/Gherkin style of tests as seen under `features/`, this is why there is intentionally no `tests/` folder. - **Test execution via nox**: Run every unit, integration, Behave, Robot, and benchmark suite exclusively through the designated `nox` sessions (e.g., `nox -s unit_tests`, `nox -s integration_tests`). Do not invoke `behave`, `robot`, or similar runners directly; if a `nox` session is missing required tooling, add the dependency to the session before rerunning. @@ -38,27 +39,26 @@ When connected to a **CleverAgents server** (developed independently), the clien While CleverAgents leverages LangGraph and LangChain for the underlying LLM runtime primitives (tool calling, graphs, routing), its value lies in what it builds on top: -* **CleverAgents** provides: - - * A **first-class plan lifecycle** (Action/Strategize/Execute/Apply) for breaking down and tracking complex work, - * A **project + resource model** for grounding tasks in real codebases, databases, documents, and infrastructure, - * A consistent **actor abstraction** for defining and composing intelligent agents, - * A consistent **skill abstraction** for anything an agent can execute, - * A **sandbox + checkpoint** safety model for safe, reversible execution, - * A **CLI/TUI/Web UX** for controlling and monitoring large multi-step autonomous work. +- **CleverAgents** provides: + - A **first-class plan lifecycle** (Action/Strategize/Execute/Apply) for breaking down and tracking complex work, + - A **project + resource model** for grounding tasks in real codebases, databases, documents, and infrastructure, + - A consistent **actor abstraction** for defining and composing intelligent agents, + - A consistent **skill abstraction** for anything an agent can execute, + - A **sandbox + checkpoint** safety model for safe, reversible execution, + - A **CLI/TUI/Web UX** for controlling and monitoring large multi-step autonomous work. ### Key Concepts -| Concept | Definition | -|---------|------------| -| **Plan** | A tracked lifecycle for a single unit-of-work (which may spawn subplans). Phases: Action -> Strategize -> Execute -> Apply | -| **Action** | A reusable plan template. Created via CLI commands (NOT YAML files). | -| **Actor** | Anything conversational; may be a single agent/LLM or an entire graph. Defined via YAML configuration files. Always named `/`. | -| **Project** | A collection of resources + configuration. Created via CLI commands (NOT YAML files). | -| **Resource** | Anything that can be read/written/queried. Each resource defines its own sandbox strategy. | -| **Skill** | A callable capability defined inline in actor YAML as tool nodes. | -| **Namespace** | Scoping mechanism: `local/`, `/`, `/`, or provider namespaces (`openai/`, `anthropic/`). | -| **Decision** | A recorded choice point made during Strategize that affects downstream work. Forms a tree enabling correction and replay. | +| Concept | Definition | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| **Plan** | A tracked lifecycle for a single unit-of-work (which may spawn subplans). Phases: Action -> Strategize -> Execute -> Apply | +| **Action** | A reusable plan template. Created via CLI commands (NOT YAML files). | +| **Actor** | Anything conversational; may be a single agent/LLM or an entire graph. Defined via YAML configuration files. Always named `/`. | +| **Project** | A collection of resources + configuration. Created via CLI commands (NOT YAML files). | +| **Resource** | Anything that can be read/written/queried. Each resource defines its own sandbox strategy. | +| **Skill** | A callable capability defined inline in actor YAML as tool nodes. | +| **Namespace** | Scoping mechanism: `local/`, `/`, `/`, or provider namespaces (`openai/`, `anthropic/`). | +| **Decision** | A recorded choice point made during Strategize that affects downstream work. Forms a tree enabling correction and replay. | --- @@ -74,18 +74,21 @@ While CleverAgents leverages LangGraph and LangChain for the underlying LLM runt ### Core Architectural Requirements **Scalability**: The system must handle massive codebases (50,000+ files) through: + - Three-tier memory architecture (hot/warm/cold) - Hierarchical task decomposition - Bounded dependency closures - Lazy resource sandboxing **Reliability**: Prevent cascading failures through: + - Complete execution isolation via sandboxes - Multi-layer semantic error prevention - Checkpoint-based rollback capabilities - Invariant enforcement throughout execution **Autonomy with Control**: Progressive automation through: + - Three-level automation system (manual, review-before-apply, full) - Decision correction without full re-execution - Confidence-based escalation @@ -94,6 +97,7 @@ While CleverAgents leverages LangGraph and LangChain for the underlying LLM runt --- ## Continuous Testing and Documentation Policy + - Do not mark any parent checklist item complete until **all** subordinate Code, Document, Tests tasks and any generated `Fix – …` tasks are resolved and the associated Notes section has the latest context. - Every time new information appears, extend the corresponding Notes section immediately with explicit references to code locations and decisions. - Maintain a running catalog of Behave commands, Robot suites, fixtures, and environments in the Notes sections to assist subsequent contributors. @@ -102,6 +106,7 @@ While CleverAgents leverages LangGraph and LangChain for the underlying LLM runt --- ## Completion Criteria + The implementation concludes only when every checklist item and spawned remediation task is checked, all Notes sections contain final decisions and references, and the full Behave and Robot test suites (unit, integration, end-to-end, benchmarking, packaging, documentation) pass without outstanding failures. --- @@ -115,11 +120,11 @@ Action -> Strategize -> Execute -> Apply -> Applied (terminal) ``` | Current Phase | Command Verb | Next Phase | -|---------------|--------------|------------| -| (none) | `create` | Action | -| Action | `use` | Strategize | -| Strategize | `execute` | Execute | -| Execute | `apply` | Applied | +| ------------- | ------------ | ---------- | +| (none) | `create` | Action | +| Action | `use` | Strategize | +| Strategize | `execute` | Execute | +| Execute | `apply` | Applied | ### Plan States (Per Phase) @@ -130,24 +135,28 @@ Action -> Strategize -> Execute -> Apply -> Applied (terminal) ### Key Architectural Components **Multi-tier Memory System**: + - **Hot tier**: Immediate working context in LLM context window - **Warm tier**: Recent decisions and contexts from current plan tree - **Cold tier**: Historical decisions from past plans, queryable but not in active memory - Context snapshots with cryptographic hashes preserve complete decision context **Dependency Closure Computation**: + - Resource-aware analysis during Strategize - Hierarchical scoping with explicit resource lists - Lazy expansion prevents closure explosion - Interface-based boundaries for modular changes **Execution Coordination**: + - Complete isolation via per-plan sandboxes - Resource-specific sandbox strategies (git worktrees, transactions, etc.) - Hierarchical merge resolution - Checkpoint-based coordination for rollback **Semantic Error Prevention**: + - Decision-time validation during Strategize - Execution-time semantic guards in actors - Invariant enforcement throughout @@ -155,12 +164,12 @@ Action -> Strategize -> Execute -> Apply -> Applied (terminal) ### Namespace Rules -| Namespace | Scope | Storage | -|-----------|-------|---------| -| `local/` | Current machine only | Local database | -| `/` | Personal server namespace | Server database | -| `/` | Organization namespace | Server database | -| `openai/`, `anthropic/`, etc. | Built-in LLM actors | N/A (built-in) | +| Namespace | Scope | Storage | +| ----------------------------- | ------------------------- | --------------- | +| `local/` | Current machine only | Local database | +| `/` | Personal server namespace | Server database | +| `/` | Organization namespace | Server database | +| `openai/`, `anthropic/`, etc. | Built-in LLM actors | N/A (built-in) | ### Configuration Philosophy @@ -177,14 +186,14 @@ All environment variables needed during testing are stored in the `.env` file in ### Current Environment Variables -| Variable Name | Service | Usage | -|--------------|---------|-------| -| `OPENROUTER_API_KEY` | OpenRouter | Access to multiple LLM models through OpenRouter API | -| `OPENAI_API_KEY` | OpenAI | Direct access to OpenAI models (GPT-3.5, GPT-4, etc.) | -| `ANTHROPIC_API_KEY` | Anthropic | Access to Claude models | -| `GOOGLE_API_KEY` | Google AI | Access to Google's API for web searches | -| `GEMINI_API_KEY` | Google Gemini | Access to Google's Gemini models | -| `HF_TOKEN` | Hugging Face | Access to Hugging Face models and datasets | +| Variable Name | Service | Usage | +| -------------------- | ------------- | ----------------------------------------------------- | +| `OPENROUTER_API_KEY` | OpenRouter | Access to multiple LLM models through OpenRouter API | +| `OPENAI_API_KEY` | OpenAI | Direct access to OpenAI models (GPT-3.5, GPT-4, etc.) | +| `ANTHROPIC_API_KEY` | Anthropic | Access to Claude models | +| `GOOGLE_API_KEY` | Google AI | Access to Google's API for web searches | +| `GEMINI_API_KEY` | Google Gemini | Access to Google's Gemini models | +| `HF_TOKEN` | Hugging Face | Access to Hugging Face models and datasets | --- @@ -237,18 +246,20 @@ All 10 ADRs have been created and package structure established. See the Phase 1 The following work from the previous implementation has been completed and will be preserved/adapted: #### Completed Infrastructure -- [X] LangChain/LangGraph dependencies and integration (ADR-011) -- [X] PlanGenerationGraph, ContextAnalysisAgent, AutoDebugGraph workflows -- [X] Memory service with EntityMemory -- [X] SQLite persistence with Alembic migrations -- [X] CLI streaming integration -- [X] Provider adapters (OpenAI, Anthropic, Google, OpenRouter) -- [X] Actor configuration system (Stage 7.5) -- [X] Test coverage at 95% + +- [x] LangChain/LangGraph dependencies and integration (ADR-011) +- [x] PlanGenerationGraph, ContextAnalysisAgent, AutoDebugGraph workflows +- [x] Memory service with EntityMemory +- [x] SQLite persistence with Alembic migrations +- [x] CLI streaming integration +- [x] Provider adapters (OpenAI, Anthropic, Google, OpenRouter) +- [x] Actor configuration system (Stage 7.5) +- [x] Test coverage at 95% #### Phase 2 Notes (Preserved from Previous Work) **2025-11-22**: Week 12 Complete, Phase 2 Core Functionality DONE + - CLI Streaming Integration fully implemented - AutoDebugGraph Implementation complete - Mock provider enhancements with configurable failure modes @@ -260,56 +271,58 @@ The following work from the previous implementation has been completed and will **2025-12-17**: Stage 7 performance optimization complete **2026-02-02**: Stage 7.5 Actor Configuration System complete **2026-02-05**: Stage A1 & A2 Complete - Plan and Action Domain Models + - Created `src/cleveragents/domain/models/core/plan.py` with: - - `PlanPhase` enum (ACTION, STRATEGIZE, EXECUTE, APPLY, APPLIED) - - `ActionState` enum (AVAILABLE, DRAFT, ARCHIVED) - - `ProcessingState` enum (QUEUED, PROCESSING, ERRORED, COMPLETE, CANCELLED) - - `NamespacedName` model with parse() and str() methods - - `PlanIdentity` model with ULID validation - - `Plan` model with full lifecycle support - - `can_transition()` function for phase transition validation + - `PlanPhase` enum (ACTION, STRATEGIZE, EXECUTE, APPLY, APPLIED) + - `ActionState` enum (AVAILABLE, DRAFT, ARCHIVED) + - `ProcessingState` enum (QUEUED, PROCESSING, ERRORED, COMPLETE, CANCELLED) + - `NamespacedName` model with parse() and str() methods + - `PlanIdentity` model with ULID validation + - `Plan` model with full lifecycle support + - `can_transition()` function for phase transition validation - Created `src/cleveragents/domain/models/core/action.py` with: - - `ActionArgument` model with parse() method for CLI argument parsing - - `Action` model with strategy/execution actor references - - Argument validation including type checking + - `ActionArgument` model with parse() method for CLI argument parsing + - `Action` model with strategy/execution actor references + - Argument validation including type checking - Added 52 Behave test scenarios across 2 feature files: - - `features/plan_model.feature` (30 scenarios) - - `features/action_model.feature` (22 scenarios) + - `features/plan_model.feature` (30 scenarios) + - `features/action_model.feature` (22 scenarios) - All new tests pass, existing tests unaffected **2026-02-05**: Stage A3 Complete - PlanLifecycleService + - Created `src/cleveragents/application/services/plan_lifecycle_service.py` with: - - Full plan lifecycle management (Action -> Strategize -> Execute -> Apply -> Applied) - - Action CRUD operations (create, get, list, make_available, archive) - - Plan creation via `use_action()` which transitions Action to Strategize - - Phase transition methods: `execute_plan()`, `apply_plan()` - - State management: `start_*()`, `complete_*()`, `fail_*()` for each phase - - `cancel_plan()` for non-terminal plans - - Custom exceptions: `InvalidPhaseTransitionError`, `ActionNotAvailableError`, `PlanNotReadyError` - - In-memory storage (to be replaced with persistence in Stage A5) + - Full plan lifecycle management (Action -> Strategize -> Execute -> Apply -> Applied) + - Action CRUD operations (create, get, list, make_available, archive) + - Plan creation via `use_action()` which transitions Action to Strategize + - Phase transition methods: `execute_plan()`, `apply_plan()` + - State management: `start_*()`, `complete_*()`, `fail_*()` for each phase + - `cancel_plan()` for non-terminal plans + - Custom exceptions: `InvalidPhaseTransitionError`, `ActionNotAvailableError`, `PlanNotReadyError` + - In-memory storage (to be replaced with persistence in Stage A5) - Added python-ulid dependency for ULID generation - Added 29 Behave test scenarios in `features/plan_lifecycle_service.feature` - Total new test scenarios: 81 (30 + 22 + 29) **2026-02-05**: Stage A4 In Progress - Plan CLI Commands + - Created `src/cleveragents/cli/commands/action.py` with: - - `agents [--data-dir PATH] [--config-path PATH] action create` - Create new action with strategy/execution actors, definition of done, arguments - - `agents [--data-dir PATH] [--config-path PATH] action list` - List actions with filtering by namespace, state - - `agents [--data-dir PATH] [--config-path PATH] action show` - Show action details by ID or name - - `agents [--data-dir PATH] [--config-path PATH] action available` - Make draft action available for use - - `agents [--data-dir PATH] [--config-path PATH] action archive` - Archive an action (soft delete) + - `agents [--data-dir PATH] [--config-path PATH] action create` - Create new action with strategy/execution actors, definition of done, arguments + - `agents [--data-dir PATH] [--config-path PATH] action list` - List actions with filtering by namespace, state + - `agents [--data-dir PATH] [--config-path PATH] action show` - Show action details by ID or name + - `agents [--data-dir PATH] [--config-path PATH] action available` - Make draft action available for use + - `agents [--data-dir PATH] [--config-path PATH] action archive` - Archive an action (soft delete) - Extended `src/cleveragents/cli/commands/plan.py` with v3 lifecycle commands: - - `agents [--data-dir PATH] [--config-path PATH] plan use ` - Use action to create plan in Strategize phase (legacy `--project` retained in old notes) + - `agents [--data-dir PATH] [--config-path PATH] plan use --project ` - Use action to create plan in Strategize phase - `agents [--data-dir PATH] [--config-path PATH] plan execute [plan_id]` - Transition plan from Strategize to Execute - `agents [--data-dir PATH] [--config-path PATH] plan apply [plan_id]` - Transition plan from Execute to Apply - `agents [--data-dir PATH] [--config-path PATH] plan status [plan_id]` - Show v3 plan status and details - - `agents [--data-dir PATH] [--config-path PATH] plan list [--phase ] [--state ] [--project ] [--action ]` - List v3 lifecycle plans with filtering + - `agents [--data-dir PATH] [--config-path PATH] plan list` - List v3 lifecycle plans with filtering - `agents [--data-dir PATH] [--config-path PATH] plan cancel ` - Cancel a non-terminal plan - Registered action commands in CLI main.py - Added 15 Behave test scenarios in `features/action_cli.feature` - Total test scenarios: 96 (81 + 15) - **2026-02-09**: Task Q0.6b Complete - README.md Setup Instructions [Brent] - Updated README.md Quick Start: added `dev` extras to `pip install`, added `scripts/setup-dev.sh` step @@ -328,18 +341,18 @@ The following work from the previous implementation has been completed and will - Fixed all 200 ruff lint findings in `features/` directory -> **0 findings** - **Config-level suppressions** (168 findings): - - Added `per-file-ignores` in `pyproject.toml` for Behave-specific patterns: - - `features/steps/*.py`: F811 (65 redefined `step_impl` — Behave idiom), E501 (long step decorator strings) - - `features/mocks/*.py`, `features/environment.py`: E501 + - Added `per-file-ignores` in `pyproject.toml` for Behave-specific patterns: + - `features/steps/*.py`: F811 (65 redefined `step_impl` — Behave idiom), E501 (long step decorator strings) + - `features/mocks/*.py`, `features/environment.py`: E501 - **Manual fixes** (31 findings across 18 files): - - 11x SIM115: `NamedTemporaryFile` refactored to use `with` context manager (`actor_cli_steps.py`, `actor_cli_run_steps.py`) - - 4x UP028: `for/yield` -> `yield from` (google, openai, openrouter, langchain provider steps) - - 3x SIM117: Nested `with` -> single `with` with parenthesized contexts (`plan_full_coverage_steps.py`, `plan_service_steps.py`) - - 3x RUF005: `list + [item]` -> `[*list, item]` unpacking - - 2x B904: Added `from exc` to `raise` inside `except` (enums, retry patterns) - - 2x RUF012: Added `ClassVar` annotations (`vector_store_service_steps.py`) - - 2x SIM105: `try/except/pass` -> `contextlib.suppress(Exception)` - - 1x each: B007 (unused loop var), B018 (noqa suppression), F821 (missing `Any` import), SIM102 (collapsible if), I001 (auto-fixed unsorted import) + - 11x SIM115: `NamedTemporaryFile` refactored to use `with` context manager (`actor_cli_steps.py`, `actor_cli_run_steps.py`) + - 4x UP028: `for/yield` -> `yield from` (google, openai, openrouter, langchain provider steps) + - 3x SIM117: Nested `with` -> single `with` with parenthesized contexts (`plan_full_coverage_steps.py`, `plan_service_steps.py`) + - 3x RUF005: `list + [item]` -> `[*list, item]` unpacking + - 2x B904: Added `from exc` to `raise` inside `except` (enums, retry patterns) + - 2x RUF012: Added `ClassVar` annotations (`vector_store_service_steps.py`) + - 2x SIM105: `try/except/pass` -> `contextlib.suppress(Exception)` + - 1x each: B007 (unused loop var), B018 (noqa suppression), F821 (missing `Any` import), SIM102 (collapsible if), I001 (auto-fixed unsorted import) - **Verification**: All affected behave tests pass (155 scenarios, 0 failures) - **Files modified**: `pyproject.toml` (config), `environment.py`, and 17 step files in `features/steps/` @@ -347,15 +360,15 @@ The following work from the previous implementation has been completed and will - Fixed all 16 pre-existing bandit findings (2 HIGH, 3 MEDIUM, 11 LOW) -> **0 findings** - **Security hardening** (HIGH+MEDIUM): - - Replaced `jinja2.Environment` with `jinja2.sandbox.SandboxedEnvironment` in `yaml_template_engine.py` and `stream_router.py` — prevents template injection - - Added `_validate_code_ast()` helper: AST-based pre-validation for `exec()` in `SimpleToolAgent` — rejects imports, `exec()`/`eval()`/`compile()`/`__import__()`/`getattr()`/`setattr()` calls, global/nonlocal statements - - Added `_validate_lambda_ast()` helper: restricts transform `eval()` to lambda-only expressions via AST parsing - - Suppressed `0.0.0.0` bind default (`# nosec B104`) — intentional, configurable via `CLEVERAGENTS_SERVER_HOST` + - Replaced `jinja2.Environment` with `jinja2.sandbox.SandboxedEnvironment` in `yaml_template_engine.py` and `stream_router.py` — prevents template injection + - Added `_validate_code_ast()` helper: AST-based pre-validation for `exec()` in `SimpleToolAgent` — rejects imports, `exec()`/`eval()`/`compile()`/`__import__()`/`getattr()`/`setattr()` calls, global/nonlocal statements + - Added `_validate_lambda_ast()` helper: restricts transform `eval()` to lambda-only expressions via AST parsing + - Suppressed `0.0.0.0` bind default (`# nosec B104`) — intentional, configurable via `CLEVERAGENTS_SERVER_HOST` - **Code quality** (LOW): - - Replaced 6 `assert` statements with proper `if`/`raise` (TypeError, RuntimeError, typer.BadParameter) — asserts stripped in optimized bytecode - - Replaced `try/except/pass` with `contextlib.suppress(Exception)` (2 locations in dispose()) - - Added logging to previously-silent exception handlers (migration_runner, nodes retry loop) - - Suppressed false positive `"token_count": 0` flagged as hardcoded password (`# nosec B105`) + - Replaced 6 `assert` statements with proper `if`/`raise` (TypeError, RuntimeError, typer.BadParameter) — asserts stripped in optimized bytecode + - Replaced `try/except/pass` with `contextlib.suppress(Exception)` (2 locations in dispose()) + - Added logging to previously-silent exception handlers (migration_runner, nodes retry loop) + - Suppressed false positive `"token_count": 0` flagged as hardcoded password (`# nosec B105`) - **Files modified**: `stream_router.py`, `yaml_template_engine.py`, `settings.py`, `context_service.py`, `memory_service.py`, `retry_patterns.py`, `context.py` (CLI), `plan_service.py`, `migration_runner.py`, `nodes.py` - **Verification**: `bandit -r src/ -c pyproject.toml` → 0 findings; targeted behave tests pass; smoke tests for AST validation pass @@ -364,14 +377,14 @@ The following work from the previous implementation has been completed and will **Stage Q0 - Pre-commit Hooks:** - Created `.pre-commit-config.yaml` with 12 hooks across 5 categories: - - Branch protection: `no-commit-to-branch` (prevents commits to main) - - General checks: `check-yaml`, `check-toml`, `check-json`, `check-merge-conflict`, `check-added-large-files`, `end-of-file-fixer`, `trailing-whitespace`, `debug-statements` - - Ruff: `ruff-format` (auto-fix), `ruff` (lint with safe auto-fix) - - Pyright: local system hook running type checking on `src/` only - - Bandit: security scanning with `pyproject.toml` configuration on `src/` only - - Vulture: dead code detection with whitelist at `vulture_whitelist.py` - - Semgrep: custom rules in `.semgrep.yml` for eval/exec/os.system/pickle detection (graceful skip when not installed) - - Commitizen: conventional commit message validation at commit-msg stage + - Branch protection: `no-commit-to-branch` (prevents commits to main) + - General checks: `check-yaml`, `check-toml`, `check-json`, `check-merge-conflict`, `check-added-large-files`, `end-of-file-fixer`, `trailing-whitespace`, `debug-statements` + - Ruff: `ruff-format` (auto-fix), `ruff` (lint with safe auto-fix) + - Pyright: local system hook running type checking on `src/` only + - Bandit: security scanning with `pyproject.toml` configuration on `src/` only + - Vulture: dead code detection with whitelist at `vulture_whitelist.py` + - Semgrep: custom rules in `.semgrep.yml` for eval/exec/os.system/pickle detection (graceful skip when not installed) + - Commitizen: conventional commit message validation at commit-msg stage - Added dev dependencies to `pyproject.toml`: `pre-commit>=3.6.0`, `bandit[toml]>=1.7.5`, `vulture>=2.10`, `radon>=6.0.1` - Added `[tool.bandit]` and `[tool.vulture]` sections to `pyproject.toml` - Created `vulture_whitelist.py` for false positive suppression (exc_tb, build_data) @@ -389,9 +402,9 @@ The following work from the previous implementation has been completed and will **Stage Q1 - CI/CD Pipeline:** - Extended `.forgejo/workflows/ci.yml` with 3 new jobs: - - `security`: bandit scan (JSON report + high-severity gate) + vulture dead code detection - - `quality`: radon complexity check (grade F fails build) + JSON report - - `coverage`: behave tests with coverage measurement, fail-under=85%, XML artifact + - `security`: bandit scan (JSON report + high-severity gate) + vulture dead code detection + - `quality`: radon complexity check (grade F fails build) + JSON report + - `coverage`: behave tests with coverage measurement, fail-under=85%, XML artifact - Updated `docker` and `helm` jobs to depend on `security` (fail-fast on security issues) - Created `scripts/check-quality-gates.py` aggregating: coverage, typecheck, security, dead code, complexity - All reports uploaded as artifacts for downstream consumption @@ -399,20 +412,20 @@ The following work from the previous implementation has been completed and will **Stage Q2 - Advanced Automation:** - Created `.forgejo/workflows/nightly-quality.yml` for nightly quality monitoring: - - Runs at midnight UTC (cron: "0 0 \* \* \*") + manual trigger support - - Full lint, typecheck, security scan (all severities), dead code, complexity analysis - - Behave tests with coverage measurement - - Quality trend JSON generation with timestamp + metrics - - 90-day artifact retention for trend analysis + - Runs at midnight UTC (cron: "0 0 \* \* \*") + manual trigger support + - Full lint, typecheck, security scan (all severities), dead code, complexity analysis + - Behave tests with coverage measurement + - Quality trend JSON generation with timestamp + metrics + - 90-day artifact retention for trend analysis - Created `scripts/check-adr-compliance.py` with AST-based checks for: - - ADR-002: No threading imports in application layer - - ADR-003: Services use constructor dependency injection - - ADR-007: No direct SQLAlchemy usage in service layer + - ADR-002: No threading imports in application layer + - ADR-003: Services use constructor dependency injection + - ADR-007: No direct SQLAlchemy usage in service layer - Added `nox -s adr_compliance` session - Created `.forgejo/pull_request_template.md` with quality checklist - Created `docs/development/quality-automation.md` with full documentation: - - Quick start, pre-commit hooks reference, CI jobs table, security scanning guide - - Complexity monitoring grades, quality gates, troubleshooting + - Quick start, pre-commit hooks reference, CI jobs table, security scanning guide + - Complexity monitoring grades, quality gates, troubleshooting **New nox sessions added:** `pre_commit`, `security_scan`, `dead_code`, `complexity`, `adr_compliance` **Total files created:** 9 new files @@ -421,26 +434,26 @@ The following work from the previous implementation has been completed and will **2026-02-10**: Task 10B.4 - Quality Metrics Baseline Established [Brent] - Ran full quality suite via nox to establish current baseline: - - **Unit Tests**: 105 features, 1613 scenarios, 7555 steps - ALL PASS - - **Lint (ruff)**: 0 findings - - **Typecheck (pyright)**: 0 errors, 0 warnings - - **Security (bandit)**: 0 findings (0 HIGH, 0 MEDIUM, 0 LOW) - - **Dead Code (vulture)**: 0 findings - - **Complexity (radon)**: Average A (3.56), 981 blocks analyzed, no grade-F methods - - High complexity methods to monitor: `LegacyDataMigrator.migrate_project_data` E(37), `Action.validate_arguments` C(20), `ProviderRegistry._create_provider_llm` C(20), `ProviderRegistry.create_ai_provider` C(18), `Settings.resolve_provider_defaults` C(18) - - **Coverage**: 96% (9860 statements, 269 missing, 2852 branches, 213 branch-miss) + - **Unit Tests**: 105 features, 1613 scenarios, 7555 steps - ALL PASS + - **Lint (ruff)**: 0 findings + - **Typecheck (pyright)**: 0 errors, 0 warnings + - **Security (bandit)**: 0 findings (0 HIGH, 0 MEDIUM, 0 LOW) + - **Dead Code (vulture)**: 0 findings + - **Complexity (radon)**: Average A (3.56), 981 blocks analyzed, no grade-F methods + - High complexity methods to monitor: `LegacyDataMigrator.migrate_project_data` E(37), `Action.validate_arguments` C(20), `ProviderRegistry._create_provider_llm` C(20), `ProviderRegistry.create_ai_provider` C(18), `Settings.resolve_provider_defaults` C(18) + - **Coverage**: 96% (9860 statements, 269 missing, 2852 branches, 213 branch-miss) - Fixed pre-existing test failure: `plan_lifecycle_cli_coverage.feature` scenario "Plan lifecycle list shows project summaries" - Rich table column wrapping at narrow terminal widths caused `+1 more` text to be split across rows. Fixed by patching console width to 200 in test setup. - Fixed missing dependency: added `langchain-anthropic>=0.2.0` to `pyproject.toml` (was imported in `src/cleveragents/providers/llm/anthropic_provider.py` but not declared) **2026-02-10**: Task Q1.5 Complete - Branch Protection Rules Documentation [Brent] - Created `docs/development/ci-cd.md` (224 lines) documenting: - - Branch protection rules for `master` (required status checks, review requirements, push/force-push/deletion blocks) - - Step-by-step Forgejo branch protection setup instructions - - Review priority matrix (P0: architecture/security, P1: algorithms, P2: features, P3: tests/docs) - - CI job dependency graph and quality gates summary table - - Nightly quality monitoring reference - - Local development workflow quick-reference + - Branch protection rules for `master` (required status checks, review requirements, push/force-push/deletion blocks) + - Step-by-step Forgejo branch protection setup instructions + - Review priority matrix (P0: architecture/security, P1: algorithms, P2: features, P3: tests/docs) + - CI job dependency graph and quality gates summary table + - Nightly quality monitoring reference + - Local development workflow quick-reference - Cross-references existing `docs/development/quality-automation.md` and `.forgejo/pull_request_template.md` - Required CI checks documented: `lint`, `typecheck`, `security`, `quality`, `behave`, `coverage`, `build` - Review requirement: 1 approving review, selective depth by priority matrix @@ -448,10 +461,10 @@ The following work from the previous implementation has been completed and will **2026-02-10**: Task 10C.1 Complete - Edge Case Test Scenarios [Brent] - Created `features/edge_case_plan_scenarios.feature` (26 scenarios, 141 steps) covering: - - **Concurrent plan execution** (6 scenarios): Duplicate strategize/execute/apply start attempts, concurrent complete+fail on same phase, two plans from same action, concurrent transitions on different plans - - **Resource conflict scenarios** (7 scenarios): Read-only file MODIFY failure, overlapping CREATE+MODIFY on same path, CREATE with None content, MOVE with missing source, DELETE of already-deleted file, file paths with spaces, deeply nested directory creation - - **Validation failure chains** (6 scenarios): Multiple simultaneous argument validation failures (missing + wrong type), unknown + missing arguments combined, empty plan description rejection, ACTION phase with processing state, invalid namespace characters, invalid name characters - - **Rollback edge cases** (7 scenarios): Partial apply failure (first change persists on disk, second unchanged), errored plan cannot restart, fail preserves error message, errored plan not terminal, cancel preserves phase, errored strategize rejects execute, failed strategize rejects complete + - **Concurrent plan execution** (6 scenarios): Duplicate strategize/execute/apply start attempts, concurrent complete+fail on same phase, two plans from same action, concurrent transitions on different plans + - **Resource conflict scenarios** (7 scenarios): Read-only file MODIFY failure, overlapping CREATE+MODIFY on same path, CREATE with None content, MOVE with missing source, DELETE of already-deleted file, file paths with spaces, deeply nested directory creation + - **Validation failure chains** (6 scenarios): Multiple simultaneous argument validation failures (missing + wrong type), unknown + missing arguments combined, empty plan description rejection, ACTION phase with processing state, invalid namespace characters, invalid name characters + - **Rollback edge cases** (7 scenarios): Partial apply failure (first change persists on disk, second unchanged), errored plan cannot restart, fail preserves error message, errored plan not terminal, cancel preserves phase, errored strategize rejects execute, failed strategize rejects complete - Created `features/steps/edge_case_plan_steps.py` with step definitions for all 26 scenarios - Verified no step name collisions with existing 104 feature files - All quality gates pass: lint 0 findings, typecheck 0 errors, full suite 106 features / 1639 scenarios / 7696 steps ALL PASS @@ -459,12 +472,12 @@ The following work from the previous implementation has been completed and will **2026-02-10**: Task 10C.4 Complete - Validation Test Fixtures [Brent] - Created `features/validation_test_fixtures.feature` (34 scenarios, 81 steps) covering 6 validation domains: - - **AST security validation** (`_validate_code_ast`): 12 scenarios testing import/global/nonlocal/exec/eval/compile/**import**/getattr/setattr rejection, syntax errors, and safe code acceptance - - **Lambda AST validation** (`_validate_lambda_ast`): 4 scenarios testing valid lambda, non-lambda rejection, syntax errors, function call rejection - - **Python content sanitization** (`_sanitize_python_content`): 4 scenarios testing passthrough, code fence stripping, docstring wrapping, irrecoverable syntax (null byte) - - **Project model validation**: 6 scenarios testing invalid chars, slashes, exclamation, valid names, relative path resolution, empty name - - **Change list coercion** (`_coerce_change_list`): 4 scenarios testing empty list, mixed entries, non-list, non-change entry - - **ActionArgument parsing**: 4 scenarios testing too few parts, invalid type, invalid requirement, reserved keyword name + - **AST security validation** (`_validate_code_ast`): 12 scenarios testing import/global/nonlocal/exec/eval/compile/**import**/getattr/setattr rejection, syntax errors, and safe code acceptance + - **Lambda AST validation** (`_validate_lambda_ast`): 4 scenarios testing valid lambda, non-lambda rejection, syntax errors, function call rejection + - **Python content sanitization** (`_sanitize_python_content`): 4 scenarios testing passthrough, code fence stripping, docstring wrapping, irrecoverable syntax (null byte) + - **Project model validation**: 6 scenarios testing invalid chars, slashes, exclamation, valid names, relative path resolution, empty name + - **Change list coercion** (`_coerce_change_list`): 4 scenarios testing empty list, mixed entries, non-list, non-change entry + - **ActionArgument parsing**: 4 scenarios testing too few parts, invalid type, invalid requirement, reserved keyword name - Created `features/steps/validation_test_fixture_steps.py` with complete step definitions for all 34 scenarios - Fixed step name collisions: renamed `I create a project with name` -> `I create a project fixture with name` and `the project path should be absolute` -> `the project fixture path should be absolute` to avoid conflicts with `database_integration_steps.py` and `domain_models_steps.py` - Moved inline import (`ArgumentRequirement`, `ArgumentType`) to file-level per CONTRIBUTING.md rules @@ -472,26 +485,27 @@ The following work from the previous implementation has been completed and will - All quality gates pass: lint 0 findings, typecheck 0 errors, full suite 107 features / 1673 scenarios / 7777 steps ALL PASS **2026-02-06**: CRITICAL ARCHITECTURAL DECISION - Tool-Based Resource Modification + - **REPLACED**: OutputParser/code fence parsing approach - **WITH**: Tool-based change tracking (modern approach used by Claude Code, Cursor, Aider) - **Key changes**: - LLMs call skills/tools directly (edit_file, write_file, delete_file, etc.) - Skills operate on sandbox state directly - ChangeSet is built from skill invocation history, NOT by parsing LLM text output -- Added built-in resource skills (now C4.file/C4.search/C4.git): file ops, dir ops, search, git ops -- Added MCP skill adapter (now C7.mcp): connect to external MCP servers + - Added built-in resource skills (C3.6): file ops, dir ops, search, git ops + - Added MCP skill adapter (C3.7): connect to external MCP servers - Replaced C4 "Multi-File ChangeSet Generation" with "Tool-Based Change Tracking" - Added SkillInvocationTracker and ToolCallRouter components -- **Rationale**: +- **Rationale**: - No parsing ambiguity (is this code or explanation?) - Each operation is explicit, typed, and trackable - Supports rollback (replay inverse of recorded changes) - Resource-agnostic (works for files, databases, APIs, any resource type) - Compatible with MCP standard for external tools - See `docs/specification.md` sections: - - "Tool-Based Resource Modification (Modern Architecture)" - - "Unified Resource Abstraction Layer" - - "MCP Integration Architecture" + - "Tool-Based Resource Modification (Modern Architecture)" + - "Unified Resource Abstraction Layer" + - "MCP Integration Architecture" --- @@ -499,21 +513,22 @@ The following work from the previous implementation has been completed and will ### Milestone Overview -| Milestone | Target Date | Description | -|-----------|-------------|-------------| -| **M0: Foundation** | Day 0 (Current) | Existing LangGraph infrastructure preserved | -| **M1: Minimal Plan Lifecycle** | +7 days | Basic Action -> Strategize -> Execute -> Apply working for source code | -| **M2: Projects & Resources** | +10 days | Project/Resource CLI commands, local filesystem sandbox | -| **M3: Actors & Skills** | +14 days | YAML actor loading, skill execution, multi-file generation | -| **M4: Decision Tree** | +21 days | Decision recording during Strategize, basic correction | -| **M5: Multi-Project & Subplans** | +25 days | Subplan spawning, parallel execution | -| **M6: Large Project Autonomy** | +30 days | Handle 10K+ file projects, decision correction, deep subplan hierarchies (LOCAL MODE ONLY) | -| **M7: Server Connectivity** | +35+ days | Client-server communication for remote project support (server developed independently) | -| **M8: Full Feature Set** | +40 days | All spec features complete | +| Milestone | Target Date | Description | +| -------------------------------- | --------------- | ------------------------------------------------------------------------------------------ | +| **M0: Foundation** | Day 0 (Current) | Existing LangGraph infrastructure preserved | +| **M1: Minimal Plan Lifecycle** | +7 days | Basic Action -> Strategize -> Execute -> Apply working for source code | +| **M2: Projects & Resources** | +10 days | Project/Resource CLI commands, local filesystem sandbox | +| **M3: Actors & Skills** | +14 days | YAML actor loading, skill execution, multi-file generation | +| **M4: Decision Tree** | +21 days | Decision recording during Strategize, basic correction | +| **M5: Multi-Project & Subplans** | +25 days | Subplan spawning, parallel execution | +| **M6: Large Project Autonomy** | +30 days | Handle 10K+ file projects, decision correction, deep subplan hierarchies (LOCAL MODE ONLY) | +| **M7: Server Connectivity** | +35+ days | Client-server communication for remote project support (server developed independently) | +| **M8: Full Feature Set** | +40 days | All spec features complete | ### Critical Path to 7-Day MVP (Source Code Only) **WEEK 1 GOAL**: A minimally usable application that can: + 1. Create an action from CLI 2. Use the action on a source code project 3. Execute with sandbox isolation @@ -522,10 +537,10 @@ The following work from the previous implementation has been completed and will ``` CRITICAL PATH (Sequential): -Day 1: A5 Plan/Action Persistence + Action Arguments (Luis) +Day 1: A5 Plan/Action Persistence (Luis) Day 2: B1.core + B2.service/B3.cli Project/Resource models + CLI (Hamza) Day 3: B4.sandbox Git worktree sandbox (Luis + Hamza) -Day 4: C1.schema/C1.examples + C2.legacy + C2.loader/C2.compiler Actor schema + compilation (Aditya + Jeff) +Day 4: C1.schema/C1.examples + C2.loader/C2.compiler Actor schema + compilation (Aditya + Jeff) Day 5: C3.protocol/C3.context/C3.inline + C4.file Skill framework + file skills (Jeff) Day 6: C4.search/C4.git + C5.model/C5.router/C5.diff Change tracking + tool routing (Luis + Jeff) Day 7: C6.pipeline/C6.gating + C7.mcp + C8.providers + C9.execute/C9.apply Plan-actor integration + validation (Aditya + Jeff + Luis) @@ -591,6 +606,7 @@ MERGE POINT 3: After Day 30 (M6 - Large Project Autonomy) ## Quick Reference for Development ### Tool Commands + ```bash # Development setup pip install -e .[dev,tests,docs] # Install with all extras @@ -613,6 +629,7 @@ nox -s docs # Build documentation ``` ### Key Files and Their Purpose + - `pyproject.toml` - All project configuration (no setup.py, no requirements.txt) - `noxfile.py` - All task automation (no Makefile, no scripts/) - `features/` - Behave unit tests (no tests/ directory) @@ -620,7 +637,8 @@ nox -s docs # Build documentation - `docs/reference/` - Discovery artifacts from Phase 0 - `docs/architecture/decisions/` - ADRs from Phase 1 -### Environment Variables (CLEVERAGENTS_* only) +### Environment Variables (CLEVERAGENTS\_\* only) + ```bash # Core configuration CLEVERAGENTS_HOME=~/.cleveragents @@ -634,129 +652,6 @@ CLEVERAGENTS_TEST_MODE=true --- -## Schedule Adhereance - -### 2026-02-12 (Day 2 since kickoff on 2026-02-11) -- Milestone calendar (relative): Day 7/M1 = 2026-02-18, Day 10/M2 = 2026-02-21, Day 14/M3 = 2026-02-25, Day 21/M4 = 2026-03-04, Day 25/M5 = 2026-03-08, Day 30/M6 = 2026-03-13, Day 35/M7 = 2026-03-18. -- Current baseline vs spec: action model still states CLI-only (not YAML), PlanLifecycleService is in-memory, `plan` CLI still contains legacy tell/build/apply paths, and legacy DB models (`projects`, `plans`, `changes`) remain; these are blockers for M1 persistence + CLI alignment. -- Schedule variance: A2b/A4b/A5 + B1/B2/C0 are still open on Day 2, leaving ~5 days to M1; we are ~2-3 days behind the critical path unless persistence + resources + tool registry start in parallel today. -- Variance snapshot (Day 2): Week 1 now explicitly allocates Rui for test scaffolds alongside Jeff/Luis/Hamza; this increases parallel test throughput but does not change the critical path for A2b/A4b/A5/B1. -- Sequencing confirmation (Day 2-6): A2b.alpha + A2b.beta -> A4b.alpha -> A4b.beta -> A5.alpha -> A5.beta/A5.gamma -> A5.legacy; B1.core + C0.domain run in parallel; B1/C0 DB migrations must rebase after A5.alpha head; A4b.alpha can scaffold in parallel but only merges after A2b.alpha/beta/gamma. -- Day 2-6 allocation (compressed): Day 2 Jeff starts A2b.alpha + A5.alpha + A4b.alpha scaffolding; Hamza starts B1.core + built-in types; Luis starts A5.beta; Aditya starts C1.schema/examples; Rui starts A2b/A4b/A5/B1 test scaffolds; Brent lands Q0-Minimum. Day 3-4: Jeff finishes A2b.alpha + A4b.alpha and advances A5.alpha; Hamza finishes B1.core and starts B2.persistence; Luis completes A5.beta and starts A5.gamma; Aditya continues C1; Rui grows suites. Day 5-6: Jeff closes A5.alpha + A4b.alpha polish, prepares A5.legacy; Luis completes A5.gamma DI; Hamza advances B2.service; Rui aligns A4b.beta tests and runs nox. -- Staffing assumptions confirmed (Day 8-14): Jeff leads C9 execute/apply, Hamza owns D1/D2 decisions, Luis owns E1 subplans, Aditya owns E2.actor, Rui handles test scaffolds, Brent runs QA gates. -- Risk: Alembic head contention between A5.alpha/B1/C0 increases rebase churn; mitigation is to rebase before merge and keep a single linear Alembic head. -- Risk: A4b CLI alignment may drift from A2b domain updates; mitigation is to lock CLI outputs and keep A4b.beta tests tied to exact fields. -- Risk: Coverage/nox gates can stall merges as suites grow; mitigation is daily nox runs and early flaky-test isolation. -- Risk: Resource/tool registry schema drift can block project/skill wiring; mitigation is to finalize YAML schemas before persistence wiring. -- Risk: Large-project performance (M6) may slip if context indexing or decomposition is slow; mitigation is to run ASV benchmarks by Day 22 and enforce file/token thresholds. -- Micro-schedule (Days 1-40, block-level): - -**Days 1-7** -| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | -| --- | --- | --- | --- | --- | --- | --- | --- | -| Day 1 AM | A5.alpha migration draft | A5.beta ORM plan | B1.core models prep | C1 schema prep | A5 test scaffolds | Q0-Minimum hooks | A5.alpha draft | -| Day 1 PM | A5.alpha finalize | A5.beta ORM models | B1.core models | C1 schema start | A5 migration tests | Q0-Minimum CI | A5.alpha commit 1 | -| Day 2 AM | A2b.alpha core fields + A5.alpha-1 draft | A5.beta mapping plan | B1.core resource_type model | C1.schema start | A2b/A4b test scaffolds | Q0-Minimum hooks | A2b.alpha draft ready | -| Day 2 PM | A4b.alpha scaffolding + A5.alpha-1 finalize | A5.beta ORM models | B1.core built-in types | C1.examples start | A5 migration tests | Q0-Minimum CI | B1.core commit ready | -| Day 3 AM | A5.alpha-2/3 migrations | A5.gamma repo | B2.persistence draft | C1.schema finalize | A4b tests | Q0-Minimum coverage | A5.alpha commits 1-2 | -| Day 3 PM | A2b.alpha finalize + A4b.alpha main | A5.gamma service | B2.persistence finalize | C1.examples finalize | Robot smoke scaffolds | nox gates | A2b.alpha commit | -| Day 4 AM | A4b.alpha finalize | A5.gamma DI wiring | B2.service start | C2.loader prep | A4b.beta tests | Q0-Minimum signoff | A4b.alpha commit | -| Day 4 PM | A5.alpha-4 finalize | A5.gamma tests | B2.service | C2.loader start | A5 tests | nox gates | A5.alpha complete | -| Day 5 AM | A5.legacy prep + C0.domain start | A5.gamma finalize | B3.cli prep | C2.loader | A4b.beta finalize | QA review | A5.gamma commit | -| Day 5 PM | A5.legacy commit | A5.gamma DI polish | B3.cli start | C2.loader | Run nox | QA review | A5.legacy ready | -| Day 6 AM | C0.domain finalize | A5.gamma DI merge | B3.cli | C2.loader | A4b.beta robot | QA signoff | C0.domain commit | -| Day 6 PM | Merge/rebase window | Fixes | B3.cli tests | C2.compiler handoff | Full nox | QA signoff | M1 delta clear | -| Day 7 AM | C5.diff prep | C6.pipeline prep | B3.cli tests | C2.compiler handoff | C5/C6 test scaffolds | QA check | M1 delta close | -| Day 7 PM | M1 buffer + polish | M1 buffer + polish | M1 buffer + polish | C2.compiler handoff | Full nox | QA signoff | M1 verified | - -**Days 8-14** -| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | -| --- | --- | --- | --- | --- | --- | --- | --- | -| Day 8 AM | C9.execute wiring | C9.execute support | Prep D1 fixtures | C8.providers configs | C8/C9 test scaffolds | QA check | Execute phase draft | -| Day 8 PM | C9.execute finalize | C9.apply prep | D1.domain prep | C8.providers finalize | C9 execute tests | QA check | C8 + C9.execute ready | -| Day 9 AM | C9.apply implementation | C9.apply implementation | D1.domain start | Support C8 docs | C9.apply tests | QA check | Apply flow draft | -| Day 9 PM | C9.apply finalize | Apply review | D1.domain continue | Provider actor polish | Apply robot + nox | QA signoff | Apply flow ready | -| Day 10 AM | D1 review | D1 support | D1.domain model | Support D1 fixtures | D1 test scaffolds | QA check | Decision model draft | -| Day 10 PM | D1 review | D1 support | D1 tests + docs + nox | D1 examples polish | D1 Robot/ASV | QA signoff | D1 commit | -| Day 11 AM | D2 review | D2 support | D2.service record | D2 fixtures | D2 test scaffolds | QA check | Decision recording draft | -| Day 11 PM | D2 review | D2 support | D2 tests + nox | D2 examples | D2 Robot/ASV | QA signoff | D2 commit | -| Day 12 AM | E1 review | E1.domain model | E1 fixtures | E2.actor prep | E1 test scaffolds | QA check | E1 draft | -| Day 12 PM | E1 review | E1 tests + docs + nox | E1 fixtures | E2.actor prep | E1 Robot/ASV | QA signoff | E1 commit | -| Day 13 AM | E2.service | E2.service support | E2 fixtures | E2.actor tool | E2 test scaffolds | QA check | E2 draft | -| Day 13 PM | E2.service tests + nox | E2 support | E2 fixtures | E2.actor tests + nox | E2 Robot/ASV | QA signoff | E2 commit | -| Day 14 AM | Integration triage | Integration support | Integration support | Integration support | Full Robot + Behave | QA signoff | M3 test pass | -| Day 14 PM | Release candidate | Release candidate | Release candidate | Release candidate | Full nox | QA signoff | M3 verified | - -**Days 15-21** -| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | -| --- | --- | --- | --- | --- | --- | --- | --- | -| Day 15 AM | D5 review | D5 support | D5.db migration | Prep D5 fixtures | D5 test scaffolds | QA check | D5.db draft | -| Day 15 PM | D5 review | D5 support | D5.repo implementation | D5 docs polish | D5 Robot/ASV | QA signoff | D5.db/repo commit | -| Day 16 AM | D3.cli review | D3.cli support | D3.cli implementation | D3 fixtures | D3 test scaffolds | QA check | D3.cli draft | -| Day 16 PM | D3.cli review | D3.cli support | D3.cli tests + nox | D3 examples | D3 Robot/ASV | QA signoff | D3.cli commit | -| Day 17 AM | D4.revert implementation | D4 support | D4 fixtures | D4 docs polish | D4 test scaffolds | QA check | D4.revert draft | -| Day 17 PM | D4.revert tests + nox | D4 support | D4 fixtures | D4 examples | D4 Robot/ASV | QA signoff | D4.revert commit | -| Day 18 AM | D4.append implementation | D5.di support | D5.di wiring | D4/D5 fixtures | D4 append tests | QA check | D4.append draft | -| Day 18 PM | D4.append tests + nox | D5.di support | D5.di tests + nox | D5 docs polish | D5 Robot/ASV | QA signoff | D4.append + D5.di commits | -| Day 19 AM | E3.exec review | E3.exec implementation | E3 fixtures | E3 docs polish | E3 test scaffolds | QA check | E3.exec draft | -| Day 19 PM | E3.exec tests + nox | E3.exec support | E3 fixtures | E3 examples | E3 Robot/ASV | QA signoff | E3.exec commit | -| Day 20 AM | E4.merge implementation | E4.merge support | E4 fixtures | E4 docs polish | E4 test scaffolds | QA check | E4.merge draft | -| Day 20 PM | E4.merge tests + nox | E4.merge support | E4 fixtures | E4 examples | E4 Robot/ASV | QA signoff | E4.merge commit | -| Day 21 AM | Integration triage | Integration support | Integration support | Integration support | Full Robot + Behave | QA signoff | M4 test pass | -| Day 21 PM | Release candidate | Release candidate | Release candidate | Release candidate | Full nox | QA signoff | M4 verified | - -**Days 22-30** -| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | -| --- | --- | --- | --- | --- | --- | --- | --- | -| Day 22 AM | G1 review | G3.semantic prep | CTX1.index implementation | CTX1 fixtures | CTX1 test scaffolds | QA check | CTX1 draft | -| Day 22 PM | G1 review | G3.semantic prep | CTX1 tests + nox | CTX1 docs polish | CTX1 Robot/ASV | QA signoff | CTX1 commit | -| Day 23 AM | G1 review | G3.semantic implementation | CTX1 index tune | CTX1 fixtures | G3 test scaffolds | QA check | G3 draft | -| Day 23 PM | G1 review | G3 tests + nox | CTX1 finalize | CTX1 docs | G3 Robot/ASV | QA signoff | G3 commit | -| Day 24 AM | G1 review | G2.checkpoint prep | G4.context implementation | G4 fixtures | G4 test scaffolds | QA check | G4 draft | -| Day 24 PM | G1 review | G2.checkpoint prep | G4 tests + nox | G4 docs polish | G4 Robot/ASV | QA signoff | G4 commit | -| Day 25 AM | G1 review | G2.checkpoint implementation | G4 context tune | G4 fixtures | G2 test scaffolds | QA check | G2 draft | -| Day 25 PM | G1 review | G2 tests + nox | G4 finalize | G4 docs | G2 Robot/ASV | QA signoff | G2 commit | -| Day 26 AM | G1.decompose implementation | G3.semantic tuning | G5.estimate prep | G5 fixtures | G1 test scaffolds | QA check | G1 draft | -| Day 26 PM | G1.decompose tests + nox | G3.semantic support | G5.estimate prep | G5 docs polish | G1 Robot/ASV | QA signoff | G1 commit | -| Day 27 AM | G1 performance | G3.semantic performance | G5.estimate implementation | G5 fixtures | G5 test scaffolds | QA check | G5 draft | -| Day 27 PM | G1 performance | G3.semantic support | G5 tests + nox | G5 docs | G5 Robot/ASV | QA signoff | G5 commit | -| Day 28 AM | F0.stubs review | F0.stubs implementation | Integration support | F0 fixtures | F0 test scaffolds | QA check | F0 draft | -| Day 28 PM | F0.stubs review | F0 tests + nox | Integration support | F0 docs polish | F0 Robot/ASV | QA signoff | F0 commit | -| Day 29 AM | M6 perf triage | Perf tuning | Perf tuning | Perf fixtures | Perf tests | QA check | Perf draft | -| Day 29 PM | M6 perf triage | Perf tuning | Perf tuning | Perf docs | Full Robot/ASV | QA signoff | Perf ready | -| Day 30 AM | Integration triage | Integration support | Integration support | Integration support | Full Robot + Behave | QA signoff | M6 test pass | -| Day 30 PM | Release candidate | Release candidate | Release candidate | Release candidate | Full nox | QA signoff | M6 verified | - -**Days 31-35** -| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | -| --- | --- | --- | --- | --- | --- | --- | --- | -| Day 31 AM | F1 review | F1.client implementation | F4.remote prep | F1 fixtures | F1 test scaffolds | QA check | F1 draft | -| Day 31 PM | F1 review | F1 tests + nox | F4.remote prep | F1 docs polish | F1 Robot/ASV | QA signoff | F1 commit | -| Day 32 AM | F1 review | F1.client finalize | F4.remote prep | F1 fixtures | F1 test scaffolds | QA check | F1 finalize | -| Day 32 PM | F2 sync review | F2.sync implementation | F4.remote prep | F2 fixtures | F2 Robot/ASV | QA signoff | F2 draft | -| Day 33 AM | F2 sync review | F2 tests + nox | F4.remote implementation | F2 docs polish | F2 test scaffolds | QA check | F2 commit | -| Day 33 PM | F3.ws review | F3.ws implementation | F4.remote implementation | F3 fixtures | F3 Robot/ASV | QA signoff | F3 draft | -| Day 34 AM | F3.ws review | F3 tests + nox | F4.remote tests + nox | F3 docs polish | F3 test scaffolds | QA check | F3 commit | -| Day 34 PM | F4.remote review | F4.remote tests + nox | F4.remote finalize | F4 docs polish | F4 Robot/ASV | QA signoff | F4 commit | -| Day 35 AM | Integration triage | Integration support | Integration support | Integration support | Full Robot + Behave | QA signoff | M7 test pass | -| Day 35 PM | Release candidate | Release candidate | Release candidate | Release candidate | Full nox | QA signoff | M7 verified | - -**Days 36-40** -| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | -| --- | --- | --- | --- | --- | --- | --- | --- | -| Day 36 AM | A6.core/A6.service review | A6.service implementation | A6.cli support | A6 examples | A6 test scaffolds | QA check | A6 draft | -| Day 36 PM | A6.core/A6.service tests + nox | A6.service support | A6.cli tests + nox | A6 docs polish | A6 Robot/ASV | QA signoff | A6 commit | -| Day 37 AM | G5.estimate review | G5.estimate support | G5.estimate implementation | G5 fixtures | G5 test scaffolds | QA check | G5 draft | -| Day 37 PM | G5.estimate tests + nox | G5.estimate support | G5.estimate tests + nox | G5 docs polish | G5 Robot/ASV | QA signoff | G5 commit | -| Day 38 AM | G2.checkpoint review | G2.checkpoint implementation | G2 fixtures | G2 docs polish | G2 test scaffolds | QA check | G2 draft | -| Day 38 PM | G2.checkpoint tests + nox | G2.checkpoint support | G2 fixtures | G2 docs | G2 Robot/ASV | QA signoff | G2 commit | -| Day 39 AM | G1.decompose tuning | G3.semantic tuning | Perf fixtures | Perf docs | Perf tests | QA check | Perf draft | -| Day 39 PM | G1.decompose tests + nox | G3.semantic tests + nox | Perf fixtures | Perf docs | Perf Robot/ASV | QA signoff | Perf ready | -| Day 40 AM | Integration triage | Integration support | Integration support | Integration support | Full Robot + Behave | QA signoff | M8 test pass | -| Day 40 PM | Release candidate | Release candidate | Release candidate | Release candidate | Full nox | QA signoff | M8 verified | -- Parallelism focus: Jeff to drive A2b/A4b/A5.legacy + C0.domain; Luis to start A5.beta/A5.gamma; Hamza to start B1.core + B2.persistence; Aditya to start C1 schema/examples; Rui to start test scaffolding for A2b/A4b/A5/B1; Brent to land Q0-Minimum gates. -- Individual status: Jeff (critical path unblocker, heavy load), Luis (persistence architecture), Hamza (resource registry + project model), Aditya (actor YAML/configs), Rui (Behave/Robot/ASV scaffolding), Brent (nox/coverage/CI gates), Mike/Brian (idle/standby). - ## Implementation Checklist This comprehensive checklist tracks all implementation tasks for the CleverAgents project. Each phase item includes mandatory **Code**, **Document**, and **Tests** bullets. Only mark the parent complete when every sub-bullet (including any spawned `Fix – …` remediation tasks) is checked. @@ -765,23 +660,22 @@ This comprehensive checklist tracks all implementation tasks for the CleverAgent Execute all required tests through the appropriate `nox` sessions—never call `behave`, `robot`, or other runners directly. After touching **any** subtask, immediately add discoveries to the Notes section and update task descriptions. -**Commit Ownership Rule**: Each **COMMIT** item has exactly one owner. Every subtask (Code/Docs/Tests/Quality/Commit) must list that same owner in brackets. If a subtask truly requires a different owner, split it into a separate **COMMIT** item under the appropriate parallel group. - ### Updated Team Assignments (by Expertise) -| Developer | Strengths | Assignment Focus | Availability | -|-----------|-----------|------------------|--------------| -| **Jeff** | CTO, fastest developer, expert in everything | Critical path blockers, architecture, complex integrations, decision correction, unblocking others | PRIMARY - Available for all critical work | -| **Aditya** | Domain expert (agents/LLMs), understands hierarchical configs | Actor YAML configurations, hierarchical actor graphs, strategy/execution actors, MCP integration | HIGH - Primary on actor/skill work | -| **Rui** | Fastest developer, new to Python | Testing (Behave/Robot), simple implementations, CLI scaffolding, test fixtures | HIGH - Parallel testing track | -| **Brent** | Slow but detail-oriented | Days 1-3: Automated quality gates setup; Days 4-8: Selective review; After Day 8: Validation testing | CONTINUOUS - Independent QA track | -| **Hamza** | RDF expert, Python proficient, no LLM experience | Projects, resources, sandbox, database infrastructure, decision models, context indexing | HIGH - Infrastructure lead | -| **Luis** | Best Python architect, pedantic | Algorithms, state machines, service layer architecture, change tracking, validation pipelines | HIGH - Architecture/service layer | -| **Mike/Brian** | Sysadmins | Deployment only (minimal coding tasks) | LOW - Only deployment tasks | +| Developer | Strengths | Assignment Focus | Availability | +| -------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------- | +| **Jeff** | CTO, fastest developer, expert in everything | Critical path blockers, architecture, complex integrations, decision correction, unblocking others | PRIMARY - Available for all critical work | +| **Aditya** | Domain expert (agents/LLMs), understands hierarchical configs | Actor YAML configurations, hierarchical actor graphs, strategy/execution actors, MCP integration | HIGH - Primary on actor/skill work | +| **Rui** | Fastest developer, new to Python | Testing (Behave/Robot), simple implementations, CLI scaffolding, test fixtures | HIGH - Parallel testing track | +| **Brent** | Slow but detail-oriented | Days 1-3: Automated quality gates setup; Days 4-8: Selective review; After Day 8: Validation testing | CONTINUOUS - Independent QA track | +| **Hamza** | RDF expert, Python proficient, no LLM experience | Projects, resources, sandbox, database infrastructure, decision models, context indexing | HIGH - Infrastructure lead | +| **Luis** | Best Python architect, pedantic | Algorithms, state machines, service layer architecture, change tracking, validation pipelines | HIGH - Architecture/service layer | +| **Mike/Brian** | Sysadmins | Deployment only (minimal coding tasks) | LOW - Only deployment tasks | ### Work Assignment Philosophy **Jeff** should be assigned to: + - Any task that is blocking other developers - Complex integrations requiring deep architectural understanding - Decision correction mechanism (critical for 30-day goal) @@ -793,29 +687,30 @@ Execute all required tests through the appropriate `nox` sessions—never call ` | Day | Task IDs | Description | Blocks | |-----|----------|-------------|--------| -| **Day 1** | A5.alpha, A5.action_arguments | Plan/Action DB schema (incl. action args) | Luis (A5.beta), All downstream | -| **Day 2** | A5.gamma | Service integration + DI wiring | CLI integration (A4 tests) | -| **Day 3** | A5.legacy, B3.cleanup, C3.protocol | Remove legacy plan/project CLI + skill protocol | All skill implementations | -| **Day 4** | C3.context, C3.inline, C2.legacy | SkillContext + inline executor + v2 actor cleanup | Luis (C5.model) | -| **Day 5** | C4.file | File operation skills | Change tracking tests | -| **Day 6** | C4.search, C4.git | Search + git skills | Actor compilation (C2.compiler) | -| **Day 7** | C6.gating, C9.execute, C9.apply | Plan-actor integration + validation | MVP verification | +| **Day 1** | A5.1, A5.2, A5.5 | Plan/Action DB schema + Plan Repository | Luis (A5.3-A5.4), All downstream | +| **Day 2** | A5.7, A5.8 | Service integration + DI wiring | CLI integration (A4 tests) | +| **Day 3** | C3.1, C3.2 | Skill Protocol + Metadata | All skill implementations | +| **Day 4** | C3.3 | SkillContext (read/write/spawn) | Aditya (C3.4), Luis (C4) | +| **Day 5** | C3.6a-b | WriteFileSkill, EditFileSkill | Change tracking tests | +| **Day 6** | C3.6f-g | Skill error handling + registry | Actor compilation | +| **Day 7** | C7 | Plan-Actor integration | MVP verification | | **Day 8** | M1.1-M1.10 | MVP merge point coordination | Release v0.1.0-rc1 | -| **Day 15-16** | D4.revert | Correction Service core algorithm | Decision correction | -| **Day 17** | D4.append | Append correction mode | Re-execution | -| **Day 18** | D5.di | Decision wiring + re-exec | M4 milestone | -| **Day 19** | D5.di | Unblock E3 parallelism | Luis (E3) | +| **Day 15-16** | D4.1 | Correction Service core algorithm | Decision correction | +| **Day 17** | D4.2 | Sandbox checkpointing | Re-execution | +| **Day 18** | D4.3 | Re-execution from correction point | M4 milestone | +| **Day 19** | D4 integration | Unblock E3 parallelism | Luis (E3) | | **Day 20-21** | E3 | Parallel execution fine-tuning | Subplan merging | | **Day 22-25** | Deep subplan hierarchies | 5+ level subplan testing | M6 | | **Day 26-28** | Performance optimization | 10K+ file codebase support | Large project autonomy | | **Day 30** | M6.1-M6.10 | Large project merge point | Release v0.3.0 | **BLOCKING CHAIN**: If Jeff is unavailable, the following chains stall: -- A5.alpha → A5.beta → A5.gamma → Plan persistence (Day 1-2) -- C3.protocol → C3.context → C4.file → Skill execution (Day 3-6) -- D4.revert → D4.append → Decision correction (Day 15-18) +- A5.1 → A5.5 → A5.7 → Plan persistence (Day 1-2) +- C3.1 → C3.3 → C3.6 → Skill execution (Day 3-6) +- D4.1 → D4.2 → D4.3 → Decision correction (Day 15-18) **Aditya** should be assigned to: + - ALL actor configuration YAML files and examples - Hierarchical actor graph compositions - Strategy and execution actor templates @@ -824,6 +719,7 @@ Execute all required tests through the appropriate `nox` sessions—never call ` - Anything requiring understanding of LLM tool calling patterns **Luis** should be assigned to: + - State machine implementations (plan lifecycle, phase transitions) - Repository pattern implementations - Service layer architecture @@ -832,6 +728,7 @@ Execute all required tests through the appropriate `nox` sessions—never call ` - Should NOT be assigned to simple CRUD or UI work **Hamza** should be assigned to: + - Database schema design and Alembic migrations - Resource model and sandbox infrastructure - Context indexing and RDF graph store integration @@ -840,6 +737,7 @@ Execute all required tests through the appropriate `nox` sessions—never call ` - Should NOT be assigned to LLM/agent-specific logic **Rui** should be assigned to: + - ALL Behave test scenarios (write BEFORE implementation) - Robot integration tests - Simple CLI scaffolding @@ -847,6 +745,7 @@ Execute all required tests through the appropriate `nox` sessions—never call ` - Should ALWAYS work in parallel with feature developers **Brent** should be assigned to: + - Days 1-3: Setting up comprehensive automated quality gates - Days 4-8: Selective manual review of high-priority items only - After Day 8: High-impact validation and edge case testing with Luis @@ -857,11 +756,10 @@ Execute all required tests through the appropriate `nox` sessions—never call ` | Milestone | Day | Goal | Success Criteria | |-----------|-----|------|------------------| -| **M1: MVP** | Day 7 | Create action → Use on project → Execute with sandbox → Apply changes (source code only) | `agents [--data-dir PATH] [--config-path PATH] action create --config` + `agents [--data-dir PATH] [--config-path PATH] plan use ` + `agents [--data-dir PATH] [--config-path PATH] plan execute` + `agents [--data-dir PATH] [--config-path PATH] plan apply` working end-to-end on a git repository with sandboxed execution | -| **M2: Projects** | Day 10 | Project/Resource CLI working with git worktree sandbox | `agents [--data-dir PATH] [--config-path PATH] project create` + `agents [--data-dir PATH] [--config-path PATH] resource add git-checkout` + `agents [--data-dir PATH] [--config-path PATH] project link-resource` (B3.cli) + git worktree isolation verified | +| **M1: MVP** | Day 7 | Create action → Use on project → Execute with sandbox → Apply changes (source code only) | `agents [--data-dir PATH] [--config-path PATH] action create` + `agents [--data-dir PATH] [--config-path PATH] plan use` + `agents [--data-dir PATH] [--config-path PATH] plan execute` + `agents [--data-dir PATH] [--config-path PATH] plan apply` working end-to-end on a git repository with sandboxed execution | +| **M2: Projects** | Day 10 | Project/Resource CLI working with git worktree sandbox | `agents [--data-dir PATH] [--config-path PATH] project create` + `agents [--data-dir PATH] [--config-path PATH] project add-resource` + git worktree isolation verified | | **M3: Actors** | Day 14 | Full plan lifecycle with actors, skills, multi-file generation | Actor YAML parsed → LangGraph compiled → Skills executed → Multi-file ChangeSet produced → Validation passing → Applied | | **M4: Decisions** | Day 21 | Decision recording, tree viewing, correction mechanism | `agents [--data-dir PATH] [--config-path PATH] plan tree` shows decisions → `agents [--data-dir PATH] [--config-path PATH] plan explain` works → `agents [--data-dir PATH] [--config-path PATH] plan correct --mode=revert` re-executes from correction point | -| **M4: Decisions** | Day 21 | Invariant CLI usage | `agents [--data-dir PATH] [--config-path PATH] invariant add --project ""` + `agents [--data-dir PATH] [--config-path PATH] invariant list --project ` | | **M5: Subplans** | Day 25 | Hierarchical subplans with parallel execution and merging | Parent plan spawns 5+ subplans → Parallel execution → Three-way merge → Validation passes | | **M6: Large Projects** | Day 30 | Handle 10,000+ file projects, autonomous language porting | Can port a 500-file Python module to TypeScript using hierarchical decomposition with decision correction | | **Server Connectivity** | Beyond Day 30 | Client interfaces for server communication; server developed independently | Client-to-server API abstractions defined, but local execution only; no server implementation in this project | @@ -876,6 +774,7 @@ Execute all required tests through the appropriate `nox` sessions—never call ` 4. **Focus on Core Functionality**: All effort goes to plan lifecycle, actors, skills, sandboxing, decisions, and subplans The following Section 8 (Server Connectivity) items are explicitly **OUT OF SCOPE** for the 30-day deadline: + - Client-to-server API integration - WebSocket streaming to server - Remote project execution via server @@ -889,36 +788,43 @@ The following Section 8 (Server Connectivity) items are explicitly **OUT OF SCOP The following chains represent sequential dependencies where each item MUST complete before the next can start: **CHAIN 1: Data Layer (Days 1-3)** + ``` A5.alpha (DB migrations) → A5.beta (ORM models) → A5.gamma (repos/service/DI) ``` **CHAIN 2: Resource Layer (Days 2-4)** + ``` B1.core (Domain Models) → B2.persistence (DB tables) → B2.service (Services) → B3.cli (CLI) ``` **CHAIN 3: Sandbox Layer (Days 3-5)** + ``` B4.sandbox (Strategy + Manager) → B4.sandbox git_worktree → B4.sandbox copy_on_write ``` **CHAIN 4: Actor Layer (Days 4-7)** + ``` -C1.schema (Actor Schema) → C2.legacy (Drop v2 configs) → C2.loader (Actor Loader) → C2.compiler (Actor Compiler) → C2.refs (Reference Resolution) +C1.schema (Actor Schema) → C2.loader (Actor Loader) → C2.compiler (Actor Compiler) → C2.refs (Reference Resolution) ``` **CHAIN 5: Skill Layer (Days 5-8)** + ``` C3.protocol (Skill Protocol) → C3.context (Skill Context) → C3.inline (Inline Executor) → C4.file/C4.search/C4.git (Built-in Skills) ``` **CHAIN 6: Change Tracking (Days 6-9)** + ``` C5.model (Change Models) → C5.router (Tool Router) → C5.diff (Diff Generator) ``` **CHAIN 7: Plan-Actor Integration (Days 8-14)** + ``` C9.execute (Strategize/Execute) → C6.pipeline/C6.gating (Validation) → C9.apply (Apply + Review) ``` @@ -931,13 +837,12 @@ C9.execute (Strategize/Execute) → C6.pipeline/C6.gating (Validation) → C9.ap WEEK 1 PARALLEL TRACKS: TRACK A [Jeff - CRITICAL PATH LEAD + Luis - ARCHITECTURE]: -├── Day 1 AM: Jeff - A5.alpha DB migrations + A5.action_arguments (2-3 hours) +├── Day 1 AM: Jeff - A5.alpha DB migrations (2-3 hours) │ └── Luis can start A5.beta ORM models after schema doc review ├── Day 1 PM: Jeff - A5.gamma repositories (2-3 hours) │ └── Luis - A5.beta ORM models (parallel) ├── Day 2: Jeff - A5.gamma service integration (main blocker) │ └── Luis - A5.gamma DI wiring -├── Day 3: Jeff - A5.legacy plan CLI cleanup + B3.cleanup legacy project CLI ├── Day 3-4: Jeff - C3.protocol/C3.context/C3.inline Skill framework (CRITICAL) │ └── Luis - C5.model Change models + tracker (parallel after C3.protocol) ├── Day 5-6: Jeff - C4.file/C4.search Built-in skills (file/dir/search) @@ -959,7 +864,6 @@ TRACK B [Hamza - INFRASTRUCTURE (No LLM Knowledge Needed)]: TRACK C [Aditya - ACTORS (Domain Expert - Hierarchical Configs)]: ├── Day 4: C1.schema Actor YAML schema models │ └── Aditya writes ALL actor YAML examples (C1.examples) -├── Day 4-5: Jeff - C2.legacy remove v2 actor configs (dependency for C2.loader) ├── Day 5: C2.loader + C2.compiler Actor loading + compilation ├── Day 6: C2.refs Reference resolution + C7.mcp MCP Skill Adapter ├── Day 7: C8.providers Built-in provider actors (openai/, anthropic/, openrouter/) @@ -1136,12 +1040,14 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - **Workstream T**: Testing [Rui] - CONTINUOUS **Merge Points**: + - **Day 7 (M1)**: All workstreams coordinate for MVP verification - **Day 14 (M3)**: Full plan lifecycle integration - **Day 21 (M4)**: Decision tree and correction mechanism - **Day 30 (M6)**: Large project autonomy target **MERGE POINT DAY 14 (M3) - Full Plan Lifecycle Integration**: + ``` ├── [Jeff - 9:00 AM] M3.1: Verify full plan lifecycle end-to-end │ └── Test: action create → plan use → plan execute → plan apply @@ -1161,6 +1067,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target ``` **MERGE POINT DAY 21 (M4) - Decision Tree & Correction**: + ``` ├── [Hamza - 9:00 AM] M4.1: Verify decision recording captures context │ └── Test: Execute strategy phase, verify decisions recorded with snapshots @@ -1185,137 +1092,188 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Target: Days 0-3 (Minimum gates before merges; advanced gates after M1)** -**Parallelization rules**: -- Minimum gating (pre-commit + CI + coverage enforcement) is a merge blocker; it can run in parallel with Week 1 coding but must land before any feature branches merge. -- Advanced automation (complexity metrics, dashboards, extended security scanning) is deferred until after M1 to avoid blocking the MVP. +**CRITICAL**: This MUST be completed before other workstreams begin to ensure all code meets quality standards from the start. -**Parallel Group Q0-Minimum Gates [Brent - blocks merges]** +- [ ] **Stage Q0: Pre-commit Hooks Setup** (Day 1) **[Brent - CRITICAL]** + - [ ] Code: Create automated pre-commit quality checks + - [ ] **Q0.1** [Brent] Install and configure pre-commit framework: + - [ ] **Q0.1a** Add `pre-commit>=3.6.0` to `pyproject.toml` dev dependencies + - [ ] **Q0.1b** Create `.pre-commit-config.yaml` in project root + - [ ] **Q0.1c** Add branch protection hook to prevent commits to main + - [ ] Commit: "feat(qa): add pre-commit framework" + - [ ] **Q0.2** [Brent] Configure Ruff for formatting and linting: + - [ ] **Q0.2a** Add Ruff formatting hook with auto-fix + - [ ] **Q0.2b** Add Ruff linting hook with auto-fix for safe fixes + - [ ] **Q0.2c** Test with intentionally bad code + - [ ] Commit: "feat(qa): add ruff formatting and linting hooks" + - [ ] **Q0.3** [Brent] Add pyright type checking: + - [ ] **Q0.3a** Configure pyright hook for changed Python files + - [ ] **Q0.3b** Set to run serially (slow but thorough) + - [ ] **Q0.3c** Test with code containing type errors + - [ ] Commit: "feat(qa): add pyright type checking hook" + - [ ] **Q0.4** [Brent] Add security scanning: + - [ ] **Q0.4a** Add `bandit[toml]>=1.7.5` to dev dependencies + - [ ] **Q0.4b** Configure bandit in `pyproject.toml` + - [ ] **Q0.4c** Add bandit pre-commit hook + - [ ] **Q0.4d** Add semgrep with custom rules for eval/exec detection + - [ ] Commit: "feat(qa): add security scanning hooks" + - [ ] **Q0.5** [Brent] Add code quality checks: + - [ ] **Q0.5a** Add `vulture>=2.10` for dead code detection + - [ ] **Q0.5b** Configure vulture whitelist for false positives + - [ ] **Q0.5c** Add commit message linting for conventional commits + - [ ] Commit: "feat(qa): add code quality hooks" + - [ ] **Q0.6** [Brent] Create developer setup automation: + - [ ] **Q0.6a** Create `scripts/setup-dev.sh` to install pre-commit + - [ ] **Q0.6b** Update README.md with setup instructions + - [ ] **Q0.6c** Test on fresh checkout + - [ ] Commit: "feat(qa): add developer setup script" + - [ ] Tests: Verify all hooks work correctly + - [ ] **Q0.7** [Rui] Write script to test all pre-commit hooks: + - [ ] Test formatting fixes + - [ ] Test linting catches issues + - [ ] Test type checking blocks bad types + - [ ] Test security scanning catches eval() + - [ ] Commit: "test(qa): add pre-commit hook tests" -- [ ] **COMMIT (Owner: Brent | Group: Q0-Minimum) - Commit message: "feat(qa): add pre-commit baseline hooks"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Brent]: Add `pre-commit>=3.6.0` to `pyproject.toml` dev dependencies and ensure it is included in the `dev` extra. - - [ ] Code [Brent]: Create `.pre-commit-config.yaml` pinned to specific hook versions; include `ruff format`, `ruff check`, `pyright`, `check-merge-conflict`, `end-of-file-fixer`, `trailing-whitespace`. - - [ ] Code [Brent]: Add `pyrightconfig.json` validation to pre-commit (hook that fails if config missing or invalid). - - [ ] Code [Brent]: Add/confirm `nox -s lint` session that runs Ruff + pyright using project settings; ensure session exits non-zero on warnings. - - [ ] Code [Brent]: Add/confirm `nox -s format` session for Ruff formatting and align it with pre-commit `ruff format` behavior. - - [ ] Docs [Brent]: Update `CONTRIBUTING.md` with pre-commit install + run steps (no helper scripts). - - [ ] Tests (Behave) [Brent]: Add scenarios in `features/quality_automation.feature` that parse `.pre-commit-config.yaml`, assert required hooks are present, and verify pinned versions. - - [ ] Tests (Robot) [Brent]: Add `robot/quality_automation.robot` that runs `nox -s lint` and asserts zero failures. - - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/precommit_config_bench.py` to benchmark config parsing and hook list extraction. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Brent]: `git commit -m "feat(qa): add pre-commit baseline hooks"`. +- [ ] **Stage Q1: CI/CD Pipeline** (Day 2) **[Brent]** + - [ ] Code: Create GitHub Actions workflow for automated PR validation + - [ ] **Q1.1** [Brent] Create `.github/workflows/pr-validation.yml`: + - [ ] **Q1.1a** Set up Python 3.13 with pip caching + - [ ] **Q1.1b** Install all dependencies including dev/test + - [ ] **Q1.1c** Run pre-commit on all files + - [ ] Commit: "feat(ci): add PR validation workflow" + - [ ] **Q1.2** [Brent] Add automated checks with GitHub annotations: + - [ ] **Q1.2a** Run ruff with GitHub output format + - [ ] **Q1.2b** Parse pyright JSON output to GitHub annotations + - [ ] **Q1.2c** Parse bandit results to security annotations + - [ ] Commit: "feat(ci): add linting and type checking" + - [ ] **Q1.3** [Brent] Add test execution with coverage: + - [ ] **Q1.3a** Run `nox -s unit_tests` with XML output + - [ ] **Q1.3b** Run `nox -s coverage_report` + - [ ] **Q1.3c** Add coverage comment to PR (fail if <85%) + - [ ] **Q1.3d** Upload test artifacts + - [ ] Commit: "feat(ci): add test execution with coverage" + - [ ] **Q1.4** [Brent] Add quality gates: + - [ ] **Q1.4a** Create `scripts/check-quality-gates.py` + - [ ] **Q1.4b** Fail if coverage <85% + - [ ] **Q1.4c** Fail if any type errors + - [ ] **Q1.4d** Fail if any security issues + - [ ] **Q1.4e** Generate summary comment for PR + - [ ] Commit: "feat(ci): add quality gate enforcement" + - [ ] **Q1.5** [Brent] Document branch protection rules: + - [ ] **Q1.5a** Require PR validation to pass + - [ ] **Q1.5b** Require 1 review (selective by Brent) + - [ ] **Q1.5c** Document in `docs/development/ci-cd.md` + - [ ] Commit: "docs(ci): add branch protection guide" + - [ ] Tests: Verify CI pipeline works + - [ ] **Q1.6** [Rui] Test CI pipeline with sample PRs: + - [ ] PR with perfect code (should pass) + - [ ] PR with type errors (should fail with annotations) + - [ ] PR with low coverage (should fail with comment) + - [ ] PR with security issues (should fail) -- [ ] **COMMIT (Owner: Brent | Group: Q0-Minimum) - Commit message: "feat(ci): add nox-based PR validation workflow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Brent]: Update `.forgejo/workflows/ci.yml` (or create `.github/workflows/pr-validation.yml` if required) to install dependencies via Hatch and run `nox` (unit + integration + typecheck + lint + coverage_report). - - [ ] Code [Brent]: Ensure CI uses Python 3.13, caches pip/Hatch artifacts, and uploads `nox` logs on failure. - - [ ] Code [Brent]: Fail pipeline if any `nox` session fails or coverage <97% (explicit coverage gate). - - [ ] Docs [Brent]: Add CI usage notes in `docs/development/ci-cd.md`, including local repro commands and cache notes. - - [ ] Tests (Behave) [Brent]: Add a scenario that validates the workflow file exists and references required `nox` sessions. - - [ ] Tests (Robot) [Brent]: Add a Robot smoke test that runs the same `nox` session matrix locally and asserts zero failures. - - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/ci_yaml_parse_bench.py` to benchmark workflow parsing and key lookup. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Brent]: `git commit -m "feat(ci): add nox-based PR validation workflow"`. +- [ ] **Stage Q2: Advanced Automation** (Day 3) **[Brent]** + - [ ] Code: Set up advanced quality monitoring + - [ ] **Q2.1** [Brent] Create nightly quality workflow: + - [ ] **Q2.1a** Schedule for midnight UTC + - [ ] **Q2.1b** Run full test suite including slow tests + - [ ] **Q2.1c** Generate quality trend reports + - [ ] Commit: "feat(ci): add nightly quality checks" + - [ ] **Q2.2** [Brent] Add complexity monitoring: + - [ ] **Q2.2a** Install `radon>=6.0.1` for complexity metrics + - [ ] **Q2.2b** Add complexity checks to pre-commit + - [ ] **Q2.2c** Fail if cyclomatic complexity >10 + - [ ] Commit: "feat(qa): add complexity monitoring" + - [ ] **Q2.3** [Brent] Create quality dashboard: + - [ ] **Q2.3a** Script to aggregate metrics + - [ ] **Q2.3b** Track coverage trends + - [ ] **Q2.3c** Track type coverage + - [ ] **Q2.3d** Generate weekly reports + - [ ] Commit: "feat(qa): add quality dashboard" + - [ ] **Q2.4** [Brent] Add ADR compliance checking: + - [ ] **Q2.4a** Script to verify ADR compliance + - [ ] **Q2.4b** Check async usage per ADR-002 + - [ ] **Q2.4c** Check DI usage per ADR-003 + - [ ] **Q2.4d** Add to CI pipeline + - [ ] Commit: "feat(qa): add ADR compliance checks" + - [ ] **Q2.5** [Brent] Create PR template: + - [ ] **Q2.5a** Add `.github/pull_request_template.md` + - [ ] **Q2.5b** Include quality checklist + - [ ] **Q2.5c** Require testing description + - [ ] Commit: "feat(qa): add PR template" + - [ ] Documentation: Quality automation guide + - [ ] **Q2.6** [Brent] Document quality automation: + - [ ] **Q2.6a** Create `docs/development/quality-automation.md` + - [ ] **Q2.6b** Document all hooks and checks + - [ ] **Q2.6c** Add troubleshooting guide + - [ ] **Q2.6d** Add to developer onboarding + - [ ] Commit: "docs(qa): comprehensive quality guide" -- [ ] **COMMIT (Owner: Brent | Group: Q0-Minimum) - Commit message: "feat(qa): enforce coverage >=97%"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Brent]: Update `nox -s coverage_report` (or equivalent session) to fail when coverage <97% and emit a clear error message. - - [ ] Code [Brent]: Wire coverage threshold enforcement into CI summary output (explicit failure line for parsing). - - [ ] Docs [Brent]: Update `docs/development/testing.md` with new coverage requirement and sample output. - - [ ] Tests (Behave) [Brent]: Add a scenario that parses coverage config and asserts threshold >=97%. - - [ ] Tests (Robot) [Brent]: Add a Robot test that runs `nox -s coverage_report` and asserts pass/fail behavior. - - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/coverage_report_bench.py` for coverage report runtime baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Brent]: `git commit -m "feat(qa): enforce coverage >=97%"`. +**After Day 3**: Brent transitions to selective manual review (Days 4-8) focusing only on: +- Architectural decisions and design patterns +- Complex algorithms and business logic +- API contracts and interfaces +- Security-sensitive code paths -**Parallel Group Q0-Advanced Gates [Brent - AFTER M1]** - -- [ ] **COMMIT (Owner: Brent | Group: Q0-Advanced) - Commit message: "feat(qa): add security scanning hooks"** (After M1) - - [ ] Code [Brent]: Add `bandit[toml]>=1.7.5` and `semgrep` dev dependencies; configure rules in `pyproject.toml`. - - [ ] Code [Brent]: Add pre-commit hooks for Bandit + Semgrep with minimal safe ruleset and explicit exclude patterns. - - [ ] Code [Brent]: Add `nox -s security` session that runs Bandit + Semgrep with config files. - - [ ] Docs [Brent]: Document security scan expectations in `docs/development/quality-automation.md`. - - [ ] Tests (Behave) [Brent]: Add scenario verifying Bandit/Semgrep hooks are declared in `.pre-commit-config.yaml`. - - [ ] Tests (Robot) [Brent]: Add Robot test that runs `nox -s security` (create session if missing). - - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/security_scan_bench.py` to baseline scan runtime. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Brent]: `git commit -m "feat(qa): add security scanning hooks"`. - -- [ ] **COMMIT (Owner: Brent | Group: Q0-Advanced) - Commit message: "feat(qa): add complexity monitoring"** (After M1) - - [ ] Code [Brent]: Add `radon>=6.0.1` and a `nox -s complexity` session with threshold <=10. - - [ ] Code [Brent]: Add complexity check to CI matrix (non-blocking until M3) and print summary. - - [ ] Docs [Brent]: Document complexity thresholds and exceptions policy. - - [ ] Tests (Behave) [Brent]: Add scenario that asserts radon configuration exists. - - [ ] Tests (Robot) [Brent]: Add Robot test that runs `nox -s complexity` on a fixture module. - - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/complexity_scan_bench.py` for radon runtime baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Brent]: `git commit -m "feat(qa): add complexity monitoring"`. - -- [ ] **COMMIT (Owner: Brent | Group: Q0-Advanced) - Commit message: "docs(qa): add quality automation guide"** (After M1) - - [ ] Docs [Brent]: Create `docs/development/quality-automation.md` with hook lists, CI steps, and troubleshooting. - - [ ] Docs [Brent]: Link the guide from `README.md` and `CONTRIBUTING.md`. - - [ ] Tests (Behave) [Brent]: Add scenario verifying the guide exists and is linked. - - [ ] Tests (Robot) [Brent]: Add Robot doc build smoke test via `nox -s docs`. - - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/docs_build_bench.py` for docs build runtime baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Brent]: `git commit -m "docs(qa): add quality automation guide"`. +**After Day 8**: Brent transitions to validation testing support, working with Luis on: +- Edge case identification and testing +- Semantic validation implementation +- Performance testing for large codebases +- Integration test scenarios --- ### Section 1: Completed Foundation (Phases 0-1) [PRESERVED] -- [X] Phase 0: Discovery and Requirements Elaboration - - [X] Code: Implement Python discovery tooling for CLI, server, data contracts, supporting assets, environment variables, implicit behaviors, and parity matrix generation. - - [X] Document: Append findings, scripts, and open questions to **Phase 0 Notes** with `file_path:line_number` references. - - [X] Tests: Run Behave discovery scenarios and Robot smoke suites covering the generated artifacts. +- [x] Phase 0: Discovery and Requirements Elaboration + - [x] Code: Implement Python discovery tooling for CLI, server, data contracts, supporting assets, environment variables, implicit behaviors, and parity matrix generation. + - [x] Document: Append findings, scripts, and open questions to **Phase 0 Notes** with `file_path:line_number` references. + - [x] Tests: Run Behave discovery scenarios and Robot smoke suites covering the generated artifacts. -- [X] Phase 1: Architecture Definition - - [X] Code: Produce ADR stubs, module scaffolding, coding standard configurations, and packaging setup. - - [X] ADR-001: Python Package Layering and Module Boundaries - - [X] ADR-002: Asyncio Concurrency Model - - [X] ADR-003: Dependency Injection Framework - - [X] ADR-004: Pydantic for Data Validation - - [X] ADR-005: Error Handling Hierarchy - - [X] ADR-006: CLEVERAGENTS Environment Variables - - [X] ADR-007: Repository Pattern for Persistence - - [X] ADR-008: Provider Plugin Architecture - - [X] ADR-009: CLI Framework Selection - - [X] ADR-010: Logging and Observability - - [X] Document: Update **Phase 1 Notes** with ADR locations, dependency policies, and style guides. - - [X] Tests: Execute Ruff, pyright, Behave architecture scenarios, and Robot lint pipelines. +- [x] Phase 1: Architecture Definition + - [x] Code: Produce ADR stubs, module scaffolding, coding standard configurations, and packaging setup. + - [x] ADR-001: Python Package Layering and Module Boundaries + - [x] ADR-002: Asyncio Concurrency Model + - [x] ADR-003: Dependency Injection Framework + - [x] ADR-004: Pydantic for Data Validation + - [x] ADR-005: Error Handling Hierarchy + - [x] ADR-006: CLEVERAGENTS Environment Variables + - [x] ADR-007: Repository Pattern for Persistence + - [x] ADR-008: Provider Plugin Architecture + - [x] ADR-009: CLI Framework Selection + - [x] ADR-010: Logging and Observability + - [x] Document: Update **Phase 1 Notes** with ADR locations, dependency policies, and style guides. + - [x] Tests: Execute Ruff, pyright, Behave architecture scenarios, and Robot lint pipelines. --- ### Section 2: Preserved Work from Previous Implementation [PRESERVED] -- [X] LangChain/LangGraph Foundation - - [X] Dependencies installed (langchain, langgraph, langsmith, etc.) - - [X] ADR-011: LangChain/LangGraph Integration Patterns - - [X] Base StateGraph classes (BaseAgent, BaseStateGraph) - - [X] LangChain mock provider (FakeListLLM) +- [x] LangChain/LangGraph Foundation + - [x] Dependencies installed (langchain, langgraph, langsmith, etc.) + - [x] ADR-011: LangChain/LangGraph Integration Patterns + - [x] Base StateGraph classes (BaseAgent, BaseStateGraph) + - [x] LangChain mock provider (FakeListLLM) -- [X] Core LangGraph Workflows - - [X] PlanGenerationGraph (load_context -> analyze_requirements -> generate_plan -> validate) - - [X] ContextAnalysisAgent (5-node workflow for context analysis) - - [X] AutoDebugGraph (analyze_error -> generate_fix -> validate_fix -> apply_fix) - - [X] Memory service with EntityMemory - - [X] CLI streaming integration +- [x] Core LangGraph Workflows + - [x] PlanGenerationGraph (load_context -> analyze_requirements -> generate_plan -> validate) + - [x] ContextAnalysisAgent (5-node workflow for context analysis) + - [x] AutoDebugGraph (analyze_error -> generate_fix -> validate_fix -> apply_fix) + - [x] Memory service with EntityMemory + - [x] CLI streaming integration -- [X] Provider Integration - - [X] Provider registry - - [X] OpenAI, Anthropic, Google, OpenRouter adapters - - [X] LangSmith observability +- [x] Provider Integration + - [x] Provider registry + - [x] OpenAI, Anthropic, Google, OpenRouter adapters + - [x] LangSmith observability -- [X] Actor System (Stage 7.5) - - [X] Actor domain model with config hashing - - [X] Actor persistence (database, repository) - - [X] Actor registry (built-ins from provider registry) - - [X] Actor CLI commands (add, update, remove, list, show) - - [X] Actor-first plan/chat commands (--actor flag) - - [X] v2 format compatibility for actor configs +- [x] Actor System (Stage 7.5) + - [x] Actor domain model with config hashing + - [x] Actor persistence (database, repository) + - [x] Actor registry (built-ins from provider registry) + - [x] Actor CLI commands (add, update, remove, list, show) + - [x] Actor-first plan/chat commands (--actor flag) + - [x] v2 format compatibility for actor configs --- @@ -1325,14 +1283,14 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **WEEK 1 - CRITICAL PATH** -- [X] **Stage A1: Plan Data Model** (Day 1) - COMPLETED 2026-02-05 - - [X] Code: Create Plan domain model - - [X] Define `Plan` Pydantic model with fields (plan_id ULID, parent_plan_id, root_plan_id, attempt counter, phase, state, timestamps) - - [X] Define `PlanPhase` enum (Action, Strategize, Execute, Apply, Applied) - - [X] Define `PlanState` enum per phase (available, draft, archived, queued, processing, errored, complete, cancelled) - - [X] Add namespace support to plan naming (`[server:][namespace/]`) - - [X] Location: `src/cleveragents/domain/models/core/plan.py` - - [X] Tests: Behave scenarios for plan model validation, phase/state transitions (30 scenarios in `features/plan_model.feature`) +- [x] **Stage A1: Plan Data Model** (Day 1) - COMPLETED 2026-02-05 + - [x] Code: Create Plan domain model + - [x] Define `Plan` Pydantic model with fields (plan_id ULID, parent_plan_id, root_plan_id, attempt counter, phase, state, timestamps) + - [x] Define `PlanPhase` enum (Action, Strategize, Execute, Apply, Applied) + - [x] Define `PlanState` enum per phase (available, draft, archived, queued, processing, errored, complete, cancelled) + - [x] Add namespace support to plan naming (`[server:][namespace/]`) + - [x] Location: `src/cleveragents/domain/models/core/plan.py` + - [x] Tests: Behave scenarios for plan model validation, phase/state transitions (30 scenarios in `features/plan_model.feature`) - [X] **Stage A2: Action Model** (Day 1) - COMPLETED 2026-02-05 - [X] Code: Create Action domain model @@ -1340,949 +1298,3532 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [X] Add argument parsing for action parameters (`--arg name:type:required|optional:description`) - [X] Location: `src/cleveragents/domain/models/core/action.py` - [X] Tests: Behave scenarios for action model validation (22 scenarios in `features/action_model.feature`) - **Parallel Group A2b: Action/Plan Spec Alignment (M1-critical)** - **PARALLEL SUBTRACK A2b.alpha [Jeff]**: Action model alignment + invariants/automation metadata - **PARALLEL SUBTRACK A2b.beta [Luis]**: Plan metadata alignment + action linkage - **PARALLEL SUBTRACK A2b.gamma [Aditya]**: Action YAML schema + examples (config-first) - **SEQUENTIAL MERGE NOTE**: A2b.alpha + A2b.beta must land before A4b CLI wiring. -- [ ] **COMMIT (Owner: Jeff | Group: A2b.alpha) - Commit message: "feat(domain): align action metadata with invariants and automation profiles"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Update `src/cleveragents/domain/models/core/action.py` docstring to state actions are defined via YAML config and registered via CLI (remove "NOT YAML" wording). - - [ ] Code [Jeff]: Add `automation_profile` field (namespaced name string) to `Action` and validate `/` format + `local/` default handling. - - [ ] Code [Jeff]: Add `invariant_actor` (optional actor ref) and `invariants` list (action-scoped) with trimming, de-duplication, and empty-string rejection. - - [ ] Code [Jeff]: Add `definition_of_done_template` to preserve the pre-rendered DoD string before arg substitution; keep `definition_of_done` as rendered output. - - [ ] Code [Jeff]: Default `definition_of_done_template` to the original `definition_of_done` when omitted, and ensure Pydantic round-trip (model_dump/model_validate) preserves both fields. - - [ ] Code [Jeff]: Add `Action.render_definition_of_done()` that renders the template using validated args and raises explicit errors on missing keys. - - [ ] Code [Jeff]: Extend `ActionArgument` validation to enforce `min_value <= max_value`, regex only for string args, and default value type checks; reject defaults that violate regex. - - [ ] Code [Jeff]: Add `ActionArgument.coerce_value()` helper that converts CLI/YAML strings into typed values (int/float/bool/list) with clear errors. - - [ ] Code [Jeff]: Add `ActionArgument.from_mapping()` to parse YAML argument dicts (name/type/required/description/default/min/max/regex) and normalize them into ActionArgument instances. - - [ ] Code [Jeff]: Add `Action.to_template_context()` (or equivalent) to generate deterministic arg context for templating and preserve ordering for tests. - - [ ] Code [Jeff]: Add `Action.from_config()` to build an Action from YAML config + CLI overrides with stable argument ordering for deterministic tests. - - [ ] Code [Jeff]: Update `ActionArgument.__str__()` to surface default/min/max/regex when emitting diagnostics or CLI output. - - [ ] Code [Jeff]: Update `Action.validate_arguments()` to use default values when optional args are omitted and to include regex/min/max checks in error output. - - [ ] Code [Jeff]: Update `PlanLifecycleService.create_action()` to accept invariants, invariant_actor, automation_profile, and definition_of_done_template and pass them into the domain model. - - [ ] Docs [Jeff]: Update or create `docs/reference/action_model.md` with new fields, YAML-first guidance, and invariants/automation profile semantics. - - [ ] Tests (Behave) [Jeff]: Add scenarios in `features/action_model.feature` for invariants validation, automation profile parsing, definition_of_done_template retention, and default value coercion. - - [ ] Tests (Robot) [Jeff]: Add Robot scenario that loads action YAML and asserts invariants/automation profile fields are surfaced in CLI output (wired in A4b). - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/action_model_bench.py` to benchmark action argument parsing + template rendering. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(domain): align action metadata with invariants and automation profiles"`. -- [ ] **COMMIT (Owner: Luis | Group: A2b.beta) - Commit message: "feat(domain): align plan metadata with action linkage and automation profiles"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add `action_name` (namespaced name string) and `action_id` (ULID string) to `Plan` for traceability to the originating action. - - [ ] Code [Luis]: Replace `project_ids` with `project_names` (namespaced names) and introduce `ProjectLink` structure (name, alias, read_only) to preserve link metadata per plan. - - [ ] Code [Luis]: Add validators to enforce namespaced project names, unique aliases, and stable ordering for CLI display. - - [ ] Code [Luis]: Add `automation_profile`, `invariant_actor`, and `invariants` fields to `Plan` with explicit source tags (action/project/plan/global) and ordering rules. - - [ ] Code [Luis]: Add `arguments` map (validated JSON-serializable values) and `definition_of_done_template` capture to the plan model for later re-rendering. - - [ ] Code [Luis]: Add execution metadata placeholders: `changeset_id`, `sandbox_refs`, `validation_summary`, and `decision_root_id` (optional until later stages). - - [ ] Code [Luis]: Add `Plan.validate_immutable_fields()` to enforce automation profile immutability after `plan use` and when phase progresses beyond Strategize. - - [ ] Code [Luis]: Update `PlanLifecycleService.use_action()` to populate action linkage, arguments, invariants, automation profile, and project link metadata on the Plan. - - [ ] Code [Luis]: Update `_print_lifecycle_plan` in `src/cleveragents/cli/commands/plan.py` to render project names/aliases instead of raw IDs. - - [ ] Docs [Luis]: Update `docs/reference/plan_model.md` to document new fields, immutability rules, and action linkage. - - [ ] Tests (Behave) [Luis]: Add scenarios in `features/plan_model.feature` for automation profile lock, invariant persistence, action linkage fields, and argument serialization. - - [ ] Tests (Robot) [Luis]: Add Robot scenario to inspect `plan status` output for action linkage and automation profile fields (once CLI aligned). - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/plan_model_bench.py` for plan validation and serialization. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(domain): align plan metadata with action linkage and automation profiles"`. -- [ ] **COMMIT (Owner: Aditya | Group: A2b.gamma) - Commit message: "docs(action): add action YAML schema and examples"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Docs [Aditya]: Author `docs/schema/action.schema.yaml` with versioning, required fields, and explicit type constraints for actors, invariants, arguments, and automation profiles. - - [ ] Docs [Aditya]: Add example action configs under `examples/actions/` (simple, invariant-heavy, multi-project, estimation-actor, and read-only examples). - - [ ] Code [Aditya]: Add schema validation helper in `src/cleveragents/action/schema.py` that loads YAML, validates schema version, and returns typed data. - - [ ] Code [Aditya]: Add clear error messages for missing required fields, invalid namespaced names, and invalid argument type combos. - - [ ] Code [Aditya]: Add unit helper to normalize YAML keys (snake_case vs camelCase) before validation. - - [ ] Tests (Behave) [Aditya]: Add scenarios that load each example YAML and assert schema validation passes; add invalid schema cases (missing actor, invalid namespaced name, bad arg types). - - [ ] Tests (Robot) [Aditya]: Add a Robot smoke test that reads example YAML files and reports parse success/failure. - - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/action_schema_bench.py` for YAML schema validation throughput. - - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Aditya]: `git commit -m "docs(action): add action YAML schema and examples"`. + - [ ] **A2.1** [Luis] Extend Action model with additional fields (follow-up): + - [ ] Field `estimation_actor: str | None` - optional actor for cost/risk estimation + - [ ] Field `review_actor: str | None` - optional actor for code review + - [ ] Field `safety_profile: SafetyProfile | None` - safety constraints (DEFERRED to post-30; see Stage POST1) + - [ ] **A2.2** [Luis] Define `SafetyProfile` model (DEFERRED to post-30; see Stage POST1): + - [ ] Field `allowed_skill_categories: list[str] | None` - whitelist of skill types + - [ ] Field `require_checkpoints: bool` - require checkpointable skills + - [ ] Field `require_sandbox: bool` - require sandbox for all resources + - [ ] Field `require_human_approval: bool` - require approval at Apply + - [ ] Field `max_cost_usd: float | None` - budget cap + - [ ] Field `max_retries: int` - maximum retry attempts + - [ ] **A2.3** [Rui] Write tests for extended action model (DEFERRED to post-30; see Stage POST1): + - [ ] Scenario: Action with estimation_actor validates correctly + - [ ] Scenario: Safety profile enforced during execution -- [X] **Stage A3: Plan State Machine** (Day 1-2) - COMPLETED 2026-02-05 - - [X] Code: Implement plan lifecycle state machine - - [X] Create `PlanLifecycleService` with phase transition methods - - [X] Implement `create_action()` - creates plan in Action phase - - [X] Implement `use_action(action, projects, args)` - transitions to Strategize - - [X] Implement `execute_plan()` - transitions to Execute - - [X] Implement `apply_plan()` - transitions to Applied - - [X] Add validation for phase transitions (only valid transitions allowed) - - [X] Location: `src/cleveragents/application/services/plan_lifecycle_service.py` - - [X] Tests: Behave scenarios for all phase transitions, invalid transition errors (29 scenarios in `features/plan_lifecycle_service.feature`) +- [x] **Stage A3: Plan State Machine** (Day 1-2) - COMPLETED 2026-02-05 + - [x] Code: Implement plan lifecycle state machine + - [x] Create `PlanLifecycleService` with phase transition methods + - [x] Implement `create_action()` - creates plan in Action phase + - [x] Implement `use_action(action, projects, args)` - transitions to Strategize + - [x] Implement `execute_plan()` - transitions to Execute + - [x] Implement `apply_plan()` - transitions to Applied + - [x] Add validation for phase transitions (only valid transitions allowed) + - [x] Location: `src/cleveragents/application/services/plan_lifecycle_service.py` + - [x] Tests: Behave scenarios for all phase transitions, invalid transition errors (29 scenarios in `features/plan_lifecycle_service.feature`) - [X] **Stage A4: Plan CLI Commands** (Day 2-3) - IN PROGRESS 2026-02-05 - [X] Code: Implement plan lifecycle CLI - - [X] `agents [--data-dir PATH] [--config-path PATH] action create --config [] [--strategy-actor ] [--execution-actor ] [--definition-of-done ""] [--arg ...]` (legacy `--name` syntax kept in historical notes) + - [X] `agents [--data-dir PATH] [--config-path PATH] action create --name --strategy-actor --execution-actor --definition-of-done "" [--arg ...]` - [X] `agents [--data-dir PATH] [--config-path PATH] action list` - list available actions - [X] `agents [--data-dir PATH] [--config-path PATH] action show ` - show action details - [X] `agents [--data-dir PATH] [--config-path PATH] action available ` - make action available - [X] `agents [--data-dir PATH] [--config-path PATH] action archive ` - archive action - - [X] `agents [--data-dir PATH] [--config-path PATH] plan use [--arg name=value ...]` - create plan from action (legacy `--project` syntax kept in historical notes) + - [X] `agents [--data-dir PATH] [--config-path PATH] plan use --project [--arg name=value ...]` - create plan from action - [X] `agents [--data-dir PATH] [--config-path PATH] plan execute [plan_id]` - execute current or specified plan - [X] `agents [--data-dir PATH] [--config-path PATH] plan apply [plan_id]` - apply executed plan (v3 lifecycle) - [X] `agents [--data-dir PATH] [--config-path PATH] plan status [plan_id]` - show plan phase/state - - [X] `agents [--data-dir PATH] [--config-path PATH] plan list [--phase ] [--state ] [--project ] [--action ]` - list plans with filters + - [X] `agents [--data-dir PATH] [--config-path PATH] plan list` - list plans with phases/states - [X] `agents [--data-dir PATH] [--config-path PATH] plan cancel ` - cancel non-terminal plan - [X] Location: `src/cleveragents/cli/commands/action.py`, `src/cleveragents/cli/commands/plan.py` - [X] Tests: Behave tests for action CLI (15 scenarios in `features/action_cli.feature`) - **Parallel Group A4b: Action/Plan CLI Spec Alignment + Tests (M1-critical)** - **PARALLEL SUBTRACK A4b.alpha [Jeff]**: CLI feature alignment - **PARALLEL SUBTRACK A4b.beta [Rui]**: Behave + Robot coverage - **SEQUENTIAL MERGE NOTE**: A4b.alpha depends on A2b.alpha + A2b.beta + A2b.gamma; A4b.beta runs after A4b.alpha to lock CLI output fields and error messages. -- [ ] **COMMIT (Owner: Jeff | Group: A4b.alpha) - Commit message: "feat(cli): support action create from YAML config"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add `--config/-c` to `agents action create`; load YAML via `src/cleveragents/action/schema.py` and fail fast on schema violations. - - [ ] Code [Jeff]: Resolve `--config` paths relative to CWD and emit explicit errors for missing/unreadable files (include path in error). - - [ ] Code [Jeff]: Implement override precedence (CLI flags override YAML fields; explicit CLI empty string clears YAML value; CLI omits leave YAML as-is). - - [ ] Code [Jeff]: Validate CLI name vs YAML `name` when both provided; error on mismatch and require YAML `name` when CLI omits it. - - [ ] Code [Jeff]: Use `Action.from_config()` to merge YAML + CLI and preserve deterministic argument ordering for tests. - - [ ] Code [Jeff]: Normalize namespaced names, actor refs, and argument definitions from YAML into `ActionArgument` objects; surface errors with field path. - - [ ] Code [Jeff]: Add `--update` guard (if action exists) or explicit error per spec; ensure idempotent update path is clear. - - [ ] Code [Jeff]: When `--update` is used, preserve `action_id` and `created_at`, update `updated_at`, and surface action state in output. - - [ ] Code [Jeff]: Update `_print_action` output in `src/cleveragents/cli/commands/action.py` to show invariants, invariant_actor, and automation_profile. - - [ ] Docs [Jeff]: Update CLI reference with YAML-based action creation + override examples and failure messages. - - [ ] Tests (Behave) [Jeff]: Add scenarios in `features/action_cli.feature` covering valid config, overrides, invalid schema, missing required fields, and update conflict. - - [ ] Tests (Robot) [Jeff]: Add `robot/action_cli_from_config.robot` with end-to-end CLI flow and output assertions (includes invariants/profile display). - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/action_cli_config_bench.py` for config parsing + normalization. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(cli): support action create from YAML config"`. -- [ ] **COMMIT (Owner: Jeff | Group: A4b.alpha) - Commit message: "feat(cli): extend plan use with invariants and automation profile"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Change `agents plan use` signature to accept positional `` arguments per spec and keep `--project` as a legacy alias only until A5.legacy removal. - - [ ] Code [Jeff]: Add `--automation-profile`, `--invariant`, and `--invariant-actor` flags to `agents plan use` and plumb into PlanLifecycleService. - - [ ] Code [Jeff]: Resolve positional project names via ProjectService; error on missing projects and preserve positional ordering. - - [ ] Code [Jeff]: Parse `--arg name=value` using `ActionArgument.coerce_value()` (not heuristic int/float guessing) and reject unknown args early. - - [ ] Code [Jeff]: Resolve action name -> action_id, validate automation profile existence via AutomationProfileService, and attach plan-scoped invariants. - - [ ] Code [Jeff]: Persist resolved invariants + profile in Plan metadata for later Strategize/Execute steps; include invariant source tags. - - [ ] Docs [Jeff]: Update `docs/reference/plan_cli.md` with examples for profile + invariants, positional project usage, and error cases. - - [ ] Tests (Behave) [Jeff]: Add scenarios in `features/plan_lifecycle_cli.feature` covering profile selection, invariant validation, positional project args, and multiple projects. - - [ ] Tests (Robot) [Jeff]: Add Robot tests for plan use with invariants and automation profiles (positive + negative cases). - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/plan_use_cli_bench.py` for argument parsing and validation. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(cli): extend plan use with invariants and automation profile"`. -- [ ] **COMMIT (Owner: Rui | Group: A4b.beta) - Commit message: "test(cli): add plan lifecycle Behave and Robot coverage"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Tests (Behave) [Rui]: Add full coverage for `plan execute`, `plan apply`, `plan status`, `plan list`, `plan cancel` (success + error paths, invalid phase, missing plan, multiple plans ready). - - [ ] Tests (Robot) [Rui]: Add `robot/plan_lifecycle_cli.robot` covering end-to-end lifecycle transitions with real DB persistence. - - [ ] Docs [Rui]: Update `docs/development/testing.md` with new CLI suites and command mappings. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_cli_smoke_bench.py` for CLI argument parsing overhead. - - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "test(cli): add plan lifecycle Behave and Robot coverage"`. + - [ ] Tests: Behave tests for plan lifecycle CLI commands (pending) + - **[Rui]** Write 20 Behave scenarios in `features/plan_lifecycle_cli.feature` covering: + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan use` with valid action and project + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan use` with missing project error + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan use` with invalid action error + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan use` with argument validation + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan execute` on strategize-complete plan + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan execute` on non-strategize plan (error case) + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan apply` on execute-complete plan + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan apply` on non-execute plan (error case) + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan status` output format verification + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan list` filtering by phase + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan list` filtering by state + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan cancel` on active plan + - [ ] `agents [--data-dir PATH] [--config-path PATH] plan cancel` on already-applied plan (error case) + - [ ] Tests: Robot integration tests for CLI commands (pending) + - **[Rui]** Write Robot test suite `robot/plan_lifecycle_cli.robot` for end-to-end CLI testing -**Parallel Group A5: Plan Persistence (M1-critical)** - **PARALLEL SUBTRACK A5.alpha [Jeff]**: Alembic migrations for action/plan tables - **PARALLEL SUBTRACK A5.beta [Luis]**: SQLAlchemy models for new tables - **SEQUENTIAL AFTER alpha+beta [Jeff + Luis]**: Repositories + service integration - **PARALLEL CONTINUOUS [Rui]**: Persistence tests added inside each commit - **SEQUENTIAL NOTE**: `action_arguments` migration must land after `actions` migration; A5.legacy should land after A4b CLI alignment + A5.gamma persistence wiring. - **SEQUENTIAL NOTE**: A5.alpha migrations must match A2b fields; rebase Alembic head after A2b merges before cutting follow-on revisions. -- [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add actions and action_invariants tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add Alembic migration skeleton with explicit down_revision dependency and naming conventions for indexes/constraints. - - [ ] Code [Jeff]: Create `actions` table with ULID PK, `namespaced_name`, `namespace`, `name`, and explicit actor refs (strategy/execution/review/apply/estimation). - - [ ] Code [Jeff]: Add `action_state` enum column (draft/available/archived) with default draft and validation-friendly values. - - [ ] Code [Jeff]: Add description columns (`short_description`, `long_description`) and DoD columns (`definition_of_done`, `definition_of_done_template`). - - [ ] Code [Jeff]: Add behavioral columns (`automation_profile`, `invariant_actor`, `reusable`, `read_only`) and metadata (`tags_json`, `created_by`, timestamps). - - [ ] Code [Jeff]: Add `action_invariants` table with FK to actions, `scope` column, `invariant_text`, optional `position`, and created_at timestamp. - - [ ] Code [Jeff]: Add unique index on `actions.namespaced_name`, index on `actions.namespace`, and index on `actions.action_state` for list filters. - - [ ] Code [Jeff]: Ensure downgrade path drops indexes and tables in reverse order. - - [ ] Docs [Jeff]: Update `docs/reference/database_schema.md` with column-level details and constraints. - - [ ] Tests (Behave) [Jeff]: Add migration scenario that runs upgrade and asserts tables + indexes exist. - - [ ] Tests (Robot) [Jeff]: Add Robot migration smoke test using `nox -s db_migrate` (create session if missing). - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/db_migration_actions_bench.py` for migration runtime baseline. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(db): add actions and action_invariants tables"`. -- [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add action_arguments table"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add Alembic migration for `action_arguments` table with ULID-less FK to actions and ordered `position` for deterministic argument ordering. - - [ ] Code [Jeff]: Add columns for `name`, `arg_type`, `requirement`, `description`, `default_value_json`, `min_value`, `max_value`, `validation_pattern`. - - [ ] Code [Jeff]: Add check constraints for numeric min/max ordering and non-empty argument names. - - [ ] Code [Jeff]: Add uniqueness constraint on (action_id, name) and index on (action_id, position). - - [ ] Docs [Jeff]: Update `docs/reference/database_schema.md` with action_arguments columns and constraints. - - [ ] Tests (Behave) [Jeff]: Add migration scenario verifying action_arguments table and constraints. - - [ ] Tests (Robot) [Jeff]: Add Robot migration smoke test that inserts a row and queries it. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/db_migration_action_args_bench.py` for migration baseline. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(db): add action_arguments table"`. -- [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add lifecycle_plans and plan_projects tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add Alembic migration for `lifecycle_plans` with ULID PK and identity fields (parent_plan_id, root_plan_id, attempt). - - [ ] Code [Jeff]: Add core plan columns: `namespaced_name`, `namespace`, `description`, `definition_of_done`, `definition_of_done_template`. - - [ ] Code [Jeff]: Add lifecycle columns: `phase` enum, `processing_state` enum, `action_state` enum (nullable after Action), and timestamps for each phase. - - [ ] Code [Jeff]: Add action linkage columns (`action_id`, `action_name`) and actor refs (strategy/execution/review/apply/estimation). - - [ ] Code [Jeff]: Add policy/metadata columns (`automation_profile`, `invariant_actor`, `read_only`, `reusable`, `created_by`, `tags_json`). - - [ ] Code [Jeff]: Add execution placeholders (`changeset_id`, `sandbox_refs_json`, `validation_summary_json`, `decision_root_id`, `error_message`, `error_details_json`). - - [ ] Code [Jeff]: Add `plan_projects` table with plan_id, project_name (namespaced), alias, read_only flag, and created_at. - - [ ] Code [Jeff]: Add uniqueness constraint on (plan_id, project_name) and index on (project_name) for lookups. - - [ ] Code [Jeff]: Add indexes on `phase`, `processing_state`, and `namespace` for list filtering. - - [ ] Docs [Jeff]: Update schema docs with plan/project link rules and uniqueness constraints. - - [ ] Tests (Behave) [Jeff]: Add migration scenario verifying plan/project link table + indexes. - - [ ] Tests (Robot) [Jeff]: Add Robot test that inserts a plan/project link row and queries it. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/db_migration_plans_bench.py` for migration runtime baseline. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(db): add lifecycle_plans and plan_projects tables"`. -- [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add plan arguments and plan invariants tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add `plan_arguments` table with plan_id, name, value_json, value_type, and `position` for stable ordering. - - [ ] Code [Jeff]: Add `plan_invariants` table with plan_id, invariant_text, source_scope, optional `position`, and created_at. - - [ ] Code [Jeff]: Add uniqueness constraint on (plan_id, name) for arguments and (plan_id, invariant_text) for invariants. - - [ ] Code [Jeff]: Add index on (plan_id, position) for fast ordered retrieval. - - [ ] Docs [Jeff]: Document argument storage, JSON serialization rules, and invariant source scopes. - - [ ] Tests (Behave) [Jeff]: Add migration scenario verifying both tables and constraints. - - [ ] Tests (Robot) [Jeff]: Add Robot test that inserts a plan invariant and asserts retrieval. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/db_migration_plan_args_bench.py` for migration runtime baseline. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(db): add plan arguments and plan invariants tables"`. -- [ ] **COMMIT (Owner: Luis | Group: A5.beta) - Commit message: "feat(models): add action and lifecycle plan ORM models"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add SQLAlchemy base model mixins for ULID PKs, timestamps, and JSON columns (reused by action/plan models). - - [ ] Code [Luis]: Implement `ActionModel` with columns for namespaced_name, namespace, actor refs, DoD fields, automation_profile, invariant_actor, state, tags_json, created_by. - - [ ] Code [Luis]: Implement `ActionInvariantModel` with FK to actions, scope, invariant_text, position, and created_at. - - [ ] Code [Luis]: Implement `ActionArgumentModel` with FK to actions, name, arg_type, requirement, defaults/min/max/regex, position, and constraints. - - [ ] Code [Luis]: Implement `LifecyclePlanModel` with identity fields, phase/state/processing enums, action linkage, DoD fields, policy metadata, and execution placeholders. - - [ ] Code [Luis]: Implement `PlanProjectLinkModel` with plan_id, project_name, alias, read_only, and created_at, plus uniqueness constraint. - - [ ] Code [Luis]: Implement `PlanArgumentModel` and `PlanInvariantModel` with ordered `position` fields and constraints. - - [ ] Code [Luis]: Define ORM relationships with ordering (`order_by=position`) and cascade rules for argument/invariant collections. - - [ ] Code [Luis]: Implement `to_domain()` and `from_domain()` mappers for each model with ULID validation, enum conversion, and timestamp normalization. - - [ ] Code [Luis]: Add repository-facing helpers for filtering by namespace/phase/state and eager-load action arguments + plan links. - - [ ] Docs [Luis]: Update ORM mapping notes in `docs/reference/database_schema.md` with model field mapping table. - - [ ] Tests (Behave) [Luis]: Add scenarios for ORM round-trip serialization, enum conversions, and ordered argument persistence. - - [ ] Tests (Robot) [Luis]: Add Robot test that loads a plan and asserts field mapping correctness. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/orm_mapping_bench.py` for Action/Plan mapping throughput. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(models): add action and lifecycle plan ORM models"`. -- [ ] **COMMIT (Owner: Jeff | Group: A5.gamma) - Commit message: "feat(repo): add action and lifecycle plan repositories"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Define repository interfaces in `src/cleveragents/domain/repositories/` for ActionRepository and PlanRepository (methods + expected errors). - - [ ] Code [Jeff]: Implement ActionRepository CRUD with deterministic ordering (created_at) and filters (namespace, state, automation_profile). - - [ ] Code [Jeff]: Implement ActionRepository persistence for arguments + invariants with ordered `position` preservation. - - [ ] Code [Jeff]: Implement PlanRepository CRUD with filters (phase/state/project_name/action_name) and lookup by namespaced_name. - - [ ] Code [Jeff]: Implement PlanRepository persistence for plan_projects, plan_arguments, plan_invariants with ordered retrieval. - - [ ] Code [Jeff]: Add retry decorator to repositories; retry only on `OperationalError`, never on `IntegrityError`. - - [ ] Code [Jeff]: Add pagination parameters (`limit`, `offset`) with default ordering for list queries. - - [ ] Docs [Jeff]: Document repository interfaces, error types, and pagination guidance. - - [ ] Tests (Behave) [Jeff]: Add scenarios for repository create/get/list/update/delete guardrails, including action argument round-trips. - - [ ] Tests (Robot) [Jeff]: Add Robot test that exercises repository through service layer. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/repository_query_bench.py` for list + filter performance. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(repo): add action and lifecycle plan repositories"`. -- [ ] **COMMIT (Owner: Luis | Group: A5.gamma) - Commit message: "feat(service): persist plan lifecycle via repositories"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Update `PlanLifecycleService` to use repositories instead of in-memory dicts. - - [ ] Code [Luis]: Replace in-memory action/plan maps with repository lookups in `get_action`, `get_plan`, and list helpers. - - [ ] Code [Luis]: Persist action creation with arguments/invariants and enforce namespaced_name uniqueness. - - [ ] Code [Luis]: Persist plan creation with project link metadata (alias/read_only) and store plan_arguments/plan_invariants in same transaction. - - [ ] Code [Luis]: Wrap transitions in UnitOfWork transactions and map DB errors to domain errors (duplicate names, missing action). - - [ ] Code [Luis]: Add optimistic guards for phase transitions (validate phase/state before update; reload on conflict). - - [ ] Docs [Luis]: Update service docs to reflect persistence and remove in-memory notes. - - [ ] Tests (Behave) [Luis]: Add scenarios for persisted lifecycle transitions and error handling (duplicate names, invalid transitions). - - [ ] Tests (Robot) [Luis]: Add end-to-end test that restarts the app and re-reads plan state. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/plan_lifecycle_service_bench.py` for persistence operations. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(service): persist plan lifecycle via repositories"`. -- [ ] **COMMIT (Owner: Luis | Group: A5.gamma) - Commit message: "feat(di): wire lifecycle repos and services"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Register ActionRepository + PlanRepository in `application/container.py` and UnitOfWork. - - [ ] Code [Luis]: Register PlanLifecycleService with repository dependencies and settings in container. - - [ ] Code [Luis]: Inject lifecycle service into CLI commands; remove direct service instantiation in `action.py` and `plan.py`. - - [ ] Code [Luis]: Add container wiring tests to ensure singleton lifetimes are correct and repositories share UoW session. - - [ ] Docs [Luis]: Update DI wiring notes in `docs/architecture/decisions/adr-003.md`. - - [ ] Tests (Behave) [Luis]: Add scenarios that use container wiring for lifecycle commands. - - [ ] Tests (Robot) [Luis]: Add Robot smoke test verifying CLI uses persisted service. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/di_container_bench.py` for container resolution overhead. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(di): wire lifecycle repos and services"`. +- [ ] **Stage A5: Plan Persistence** (Day 1-2) **[Jeff + Luis - Critical Path]** + + **PARALLEL SUBTRACK A5.alpha [Jeff - Day 1 AM]**: Database Schema (A5.1, A5.2) + **PARALLEL SUBTRACK A5.beta [Luis - Day 1 AM]**: SQLAlchemy Models (A5.3, A5.4) - can start with schema design doc + **SEQUENTIAL AFTER alpha+beta [Jeff - Day 1 PM]**: Repository Implementation (A5.5, A5.6) + **SEQUENTIAL AFTER repos [Jeff - Day 2 AM]**: Service Integration (A5.7, A5.8) + **PARALLEL CONTINUOUS [Rui - Day 1-2]**: Test Writing (A5.9, A5.10, A5.11) + + - [ ] Code: Plan database schema and repository + - [ ] **A5.1** [Jeff] Create Alembic migration for `lifecycle_plans` table in `alembic/versions/xxx_add_lifecycle_plans.py`: + - [ ] **A5.1a** [Jeff] Create migration file with `revision` and `down_revision` links: + - [ ] Run `alembic revision -m "add_lifecycle_plans_table"` to generate file + - [ ] Verify revision ID is unique + - [ ] Set `down_revision` to point to previous migration (likely actions table) + - [ ] Commit: "feat(db): add lifecycle_plans migration scaffold" + - [ ] **A5.1b** [Jeff] Define `lifecycle_plans` table schema in upgrade() function: + - [ ] Column `plan_id` TEXT PRIMARY KEY (ULID format, validated at application layer) + - [ ] Column `parent_plan_id` TEXT NULLABLE FK references lifecycle_plans(plan_id) - for subplan hierarchy + - [ ] Column `root_plan_id` TEXT NULLABLE FK references lifecycle_plans(plan_id) - always points to topmost plan + - [ ] Column `action_id` TEXT NOT NULL FK references actions(action_id) - the action template used + - [ ] Column `phase` TEXT NOT NULL CHECK(phase IN ('ACTION','STRATEGIZE','EXECUTE','APPLY','APPLIED')) - lifecycle phase + - [ ] Column `state` TEXT NOT NULL - processing state within phase (available/draft/archived for ACTION; queued/processing/errored/complete/cancelled for others) + - [ ] Column `attempt` INTEGER NOT NULL DEFAULT 1 - increments on re-execution after correction + - [ ] Column `automation_level` TEXT NOT NULL DEFAULT 'manual' CHECK(automation_level IN ('manual','review_before_apply','full_automation')) + - [ ] Column `project_ids` TEXT NOT NULL - JSON array of project ULIDs this plan operates on + - [ ] Column `arguments` TEXT NULLABLE - JSON object mapping argument name to provided value + - [ ] Column `strategy_context` TEXT NULLABLE - JSON blob storing Strategize phase outputs (strategy, execution blueprint, resource queries) + - [ ] Column `execution_log` TEXT NULLABLE - JSON array of execution events [{timestamp, event_type, details}] + - [ ] Column `changeset_id` TEXT NULLABLE FK references changesets(changeset_id) - link to generated changes + - [ ] Column `sandbox_refs` TEXT NULLABLE - JSON object mapping resource_id to sandbox_path + - [ ] Column `error_message` TEXT NULLABLE - last error message if state is errored + - [ ] Column `created_at` TEXT NOT NULL - ISO8601 timestamp of plan creation + - [ ] Column `updated_at` TEXT NOT NULL - ISO8601 timestamp of last modification + - [ ] Column `completed_at` TEXT NULLABLE - ISO8601 timestamp when plan reached terminal state + - [ ] Column `created_by` TEXT NULLABLE - user/session identifier who created the plan + - [ ] Commit: "feat(db): define lifecycle_plans table columns" + - [ ] **A5.1c** [Jeff] Create indices for common queries: + - [ ] Index `ix_lifecycle_plans_phase` on `phase` - for phase-based filtering + - [ ] Index `ix_lifecycle_plans_state` on `state` - for state-based filtering + - [ ] Index `ix_lifecycle_plans_parent` on `parent_plan_id` - for subplan lookups + - [ ] Index `ix_lifecycle_plans_root` on `root_plan_id` - for full tree queries + - [ ] Index `ix_lifecycle_plans_created` on `created_at` - for recent plans + - [ ] Index `ix_lifecycle_plans_action` on `action_id` - for action usage lookups + - [ ] Index `ix_lifecycle_plans_project` on `project_ids` - for project-based queries (use json_extract if needed) + - [ ] Commit: "feat(db): add lifecycle_plans indices" + - [ ] **A5.1d** [Jeff] Define foreign key ON DELETE behaviors: + - [ ] parent_plan_id: ON DELETE SET NULL - orphan subplans if parent deleted (preserve for debugging) + - [ ] root_plan_id: ON DELETE SET NULL - same reasoning + - [ ] action_id: ON DELETE RESTRICT - cannot delete action if plans exist using it + - [ ] changeset_id: ON DELETE SET NULL - preserve plan record even if changeset cleaned up + - [ ] Commit: "feat(db): define lifecycle_plans FK constraints" + - [ ] **A5.1e** [Jeff] Write `downgrade()` function to drop table: + - [ ] Drop all indices first + - [ ] Drop the lifecycle_plans table + - [ ] Verify downgrade works with `alembic downgrade -1` + - [ ] Commit: "feat(db): add lifecycle_plans downgrade function" + - [ ] **A5.2** [Jeff] Create Alembic migration for `actions` table in `alembic/versions/xxx_add_actions.py`: + - [ ] **A5.2a** [Jeff] Create migration file: + - [ ] Run `alembic revision -m "add_actions_table"` + - [ ] This migration MUST run BEFORE lifecycle_plans (set down_revision appropriately) + - [ ] Commit: "feat(db): add actions migration scaffold" + - [ ] **A5.2b** [Jeff] Define `actions` table schema: + - [ ] Column `action_id` TEXT PRIMARY KEY - ULID format + - [ ] Column `name` TEXT NOT NULL - full namespaced name (e.g., "local/code-coverage", "myorg/deploy-action") + - [ ] Column `namespace` TEXT NOT NULL - extracted namespace portion for filtering (e.g., "local", "myorg") + - [ ] Column `short_name` TEXT NOT NULL - extracted name portion after namespace (e.g., "code-coverage") + - [ ] Column `description` TEXT NULLABLE - human-readable description + - [ ] Column `definition_of_done` TEXT NOT NULL - explicit testable completion criteria (must/should/may format) + - [ ] Column `strategy_actor` TEXT NOT NULL - namespaced actor reference for Strategize phase (e.g., "local/coverage-strategist") + - [ ] Column `execution_actor` TEXT NOT NULL - namespaced actor reference for Execute phase + - [ ] Column `estimation_actor` TEXT NULLABLE - optional actor for cost/risk estimation (runs after Strategize) + - [ ] Column `review_actor` TEXT NULLABLE - optional actor for code review + - [ ] Column `inputs_schema` TEXT NOT NULL DEFAULT '[]' - JSON array of ActionArgument definitions + - [ ] Column `state` TEXT NOT NULL DEFAULT 'draft' CHECK(state IN ('available','draft','archived')) + - [ ] Column `reusable` BOOLEAN NOT NULL DEFAULT TRUE - if false, action self-deletes after first use + - [ ] Column `read_only` BOOLEAN NOT NULL DEFAULT FALSE - if true, only read-only skills allowed + - [ ] Column `safety_profile` TEXT NULLABLE - JSON object for SafetyProfile constraints (DEFERRED to post-30; see POST1) + - [ ] Column `created_at` TEXT NOT NULL - ISO8601 creation timestamp + - [ ] Column `updated_at` TEXT NOT NULL - ISO8601 last modification timestamp + - [ ] Commit: "feat(db): define actions table columns" + - [ ] **A5.2c** [Jeff] Create indices: + - [ ] UNIQUE index on `name` - enforce unique namespaced names + - [ ] Index `ix_actions_namespace` on `namespace` - for namespace filtering + - [ ] Index `ix_actions_state` on `state` - for state filtering + - [ ] Index `ix_actions_short_name` on `short_name` - for partial name searches + - [ ] Commit: "feat(db): add actions indices" + - [ ] **A5.2d** [Jeff] Write `downgrade()` function: + - [ ] Drop indices and table + - [ ] Verify with `alembic downgrade -1` + - [ ] Commit: "feat(db): add actions downgrade function" + - [ ] **A5.3** [Luis] Create `LifecyclePlanModel` SQLAlchemy model in `src/cleveragents/infrastructure/database/models.py`: + - [ ] **A5.3a** [Luis] Define class structure: + - [ ] Create class `LifecyclePlanModel(Base)` with `__tablename__ = 'lifecycle_plans'` + - [ ] Import necessary SQLAlchemy types: `Column, String, Integer, Boolean, Text, ForeignKey, DateTime` + - [ ] Import relationship types: `relationship, backref` + - [ ] Commit: "feat(models): add LifecyclePlanModel class scaffold" + - [ ] **A5.3b** [Luis] Define all columns matching migration schema: + - [ ] `plan_id = Column(String(26), primary_key=True)` - ULID is 26 chars + - [ ] `parent_plan_id = Column(String(26), ForeignKey('lifecycle_plans.plan_id', ondelete='SET NULL'), nullable=True)` + - [ ] `root_plan_id = Column(String(26), ForeignKey('lifecycle_plans.plan_id', ondelete='SET NULL'), nullable=True)` + - [ ] `action_id = Column(String(26), ForeignKey('actions.action_id', ondelete='RESTRICT'), nullable=False)` + - [ ] `phase = Column(String(20), nullable=False)` - enum handled at domain layer + - [ ] `state = Column(String(20), nullable=False)` + - [ ] `attempt = Column(Integer, nullable=False, default=1)` + - [ ] `automation_level = Column(String(30), nullable=False, default='manual')` + - [ ] `project_ids = Column(Text, nullable=False)` - JSON string + - [ ] `arguments = Column(Text, nullable=True)` - JSON string + - [ ] `strategy_context = Column(Text, nullable=True)` - large JSON blob + - [ ] `execution_log = Column(Text, nullable=True)` - JSON array + - [ ] `changeset_id = Column(String(26), nullable=True)` + - [ ] `sandbox_refs = Column(Text, nullable=True)` - JSON object + - [ ] `error_message = Column(Text, nullable=True)` + - [ ] `created_at = Column(String(30), nullable=False)` - ISO8601 + - [ ] `updated_at = Column(String(30), nullable=False)` + - [ ] `completed_at = Column(String(30), nullable=True)` + - [ ] `created_by = Column(String(255), nullable=True)` + - [ ] Commit: "feat(models): define LifecyclePlanModel columns" + - [ ] **A5.3c** [Luis] Define relationships: + - [ ] `parent_plan = relationship('LifecyclePlanModel', remote_side=[plan_id], backref='children', foreign_keys=[parent_plan_id])` + - [ ] `action = relationship('ActionModel', backref='plans')` + - [ ] NOTE: root_plan relationship not needed as query pattern is different + - [ ] Commit: "feat(models): define LifecyclePlanModel relationships" + - [ ] **A5.3d** [Luis] Implement `to_domain() -> Plan` method: + - [ ] Import `Plan, PlanPhase, ProcessingState, AutomationLevel` from domain + - [ ] Convert `phase` string to `PlanPhase` enum: `PlanPhase[self.phase]` + - [ ] Convert `state` string to appropriate state enum based on phase + - [ ] Parse `project_ids` JSON: `json.loads(self.project_ids)` with error handling + - [ ] Parse `arguments` JSON if not None: `json.loads(self.arguments) if self.arguments else None` + - [ ] Parse `strategy_context` JSON if not None + - [ ] Parse `execution_log` JSON if not None + - [ ] Parse `sandbox_refs` JSON if not None + - [ ] Convert timestamp strings to `datetime.fromisoformat()` objects + - [ ] Construct and return `Plan(plan_id=self.plan_id, ...)` + - [ ] Add comprehensive docstring explaining the conversion + - [ ] Commit: "feat(models): implement LifecyclePlanModel.to_domain()" + - [ ] **A5.3e** [Luis] Implement classmethod `from_domain(plan: Plan) -> LifecyclePlanModel`: + - [ ] Add `@classmethod` decorator + - [ ] Convert `plan.phase.name` to string for phase column + - [ ] Convert state enum `.name` to string + - [ ] Serialize `project_ids` to JSON: `json.dumps(plan.project_ids)` + - [ ] Serialize `arguments` to JSON if not None + - [ ] Serialize `strategy_context` to JSON if not None (handle nested objects) + - [ ] Serialize `execution_log` to JSON if not None + - [ ] Serialize `sandbox_refs` to JSON if not None + - [ ] Convert datetime objects to `.isoformat()` strings + - [ ] Return constructed `LifecyclePlanModel` instance + - [ ] Commit: "feat(models): implement LifecyclePlanModel.from_domain()" + - [ ] **A5.4** [Luis] Create `ActionModel` SQLAlchemy model in `src/cleveragents/infrastructure/database/models.py`: + - [ ] **A5.4a** [Luis] Define class structure and columns: + - [ ] Create class `ActionModel(Base)` with `__tablename__ = 'actions'` + - [ ] `action_id = Column(String(26), primary_key=True)` + - [ ] `name = Column(String(255), nullable=False, unique=True)` + - [ ] `namespace = Column(String(100), nullable=False)` + - [ ] `short_name = Column(String(150), nullable=False)` + - [ ] `description = Column(Text, nullable=True)` + - [ ] `definition_of_done = Column(Text, nullable=False)` + - [ ] `strategy_actor = Column(String(255), nullable=False)` + - [ ] `execution_actor = Column(String(255), nullable=False)` + - [ ] `estimation_actor = Column(String(255), nullable=True)` + - [ ] `review_actor = Column(String(255), nullable=True)` + - [ ] `inputs_schema = Column(Text, nullable=False, default='[]')` + - [ ] `state = Column(String(20), nullable=False, default='draft')` + - [ ] `reusable = Column(Boolean, nullable=False, default=True)` + - [ ] `read_only = Column(Boolean, nullable=False, default=False)` + - [ ] `safety_profile = Column(Text, nullable=True)` (DEFERRED to post-30; see POST1) + - [ ] `created_at = Column(String(30), nullable=False)` + - [ ] `updated_at = Column(String(30), nullable=False)` + - [ ] Commit: "feat(models): define ActionModel columns" + - [ ] **A5.4b** [Luis] Implement `to_domain() -> Action` method: + - [ ] Import `Action, ActionState, ActionArgument` from domain + - [ ] Convert `state` string to `ActionState` enum + - [ ] Parse `inputs_schema` JSON and convert to `list[ActionArgument]` + - [ ] Parse `safety_profile` JSON if present to `SafetyProfile` or None (DEFERRED to post-30; see POST1) + - [ ] Convert timestamps to datetime objects + - [ ] Construct and return `Action` instance + - [ ] Commit: "feat(models): implement ActionModel.to_domain()" + - [ ] **A5.4c** [Luis] Implement classmethod `from_domain(action: Action) -> ActionModel`: + - [ ] Extract namespace and short_name from action.name using `NamespacedName.parse()` + - [ ] Serialize `inputs_schema` to JSON from list of ActionArgument (call `.model_dump()` on each) + - [ ] Serialize `safety_profile` to JSON if present (DEFERRED to post-30; see POST1) + - [ ] Convert timestamps to ISO8601 strings + - [ ] Return constructed `ActionModel` instance + - [ ] Commit: "feat(models): implement ActionModel.from_domain()" + - [ ] **A5.5** [Jeff] Implement `LifecyclePlanRepository` in `src/cleveragents/infrastructure/database/repositories.py`: + - [ ] **A5.5a** [Jeff] Define class structure: + - [ ] Create class `LifecyclePlanRepository` with proper typing + - [ ] Add `__init__(self, session_factory: Callable[[], Session])` - session factory injection + - [ ] Store `self._session_factory = session_factory` + - [ ] Add class docstring explaining repository pattern usage + - [ ] Commit: "feat(repo): add LifecyclePlanRepository scaffold" + - [ ] **A5.5b** [Jeff] Implement `create(plan: Plan) -> Plan`: + - [ ] Open session using context manager: `with self._session_factory() as session:` + - [ ] Convert domain model: `model = LifecyclePlanModel.from_domain(plan)` + - [ ] Add to session: `session.add(model)` + - [ ] Commit transaction: `session.commit()` + - [ ] Refresh to get any database-generated values: `session.refresh(model)` + - [ ] Convert back and return: `return model.to_domain()` + - [ ] Wrap in try/except for `IntegrityError` - raise custom `DuplicatePlanError` if duplicate ID + - [ ] Add type hints and docstring + - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.create()" + - [ ] **A5.5c** [Jeff] Implement `get_by_id(plan_id: str) -> Plan | None`: + - [ ] Query by primary key: `session.query(LifecyclePlanModel).filter_by(plan_id=plan_id).first()` + - [ ] Return `None` if not found + - [ ] Convert to domain model if found + - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.get_by_id()" + - [ ] **A5.5d** [Jeff] Implement `get_by_phase(phase: PlanPhase, limit: int = 100) -> list[Plan]`: + - [ ] Filter by phase column: `.filter_by(phase=phase.name)` + - [ ] Order by created_at DESC: `.order_by(LifecyclePlanModel.created_at.desc())` + - [ ] Apply limit: `.limit(limit)` + - [ ] Convert all results to domain models using list comprehension + - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.get_by_phase()" + - [ ] **A5.5e** [Jeff] Implement `get_by_state(state: ProcessingState, limit: int = 100) -> list[Plan]`: + - [ ] Similar pattern to get_by_phase + - [ ] Filter by state column + - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.get_by_state()" + - [ ] **A5.5f** [Jeff] Implement `get_children(parent_plan_id: str) -> list[Plan]`: + - [ ] Filter by parent_plan_id: `.filter_by(parent_plan_id=parent_plan_id)` + - [ ] Order by created_at ASC (oldest first for processing order) + - [ ] Convert all to domain models + - [ ] Used for listing direct subplans + - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.get_children()" + - [ ] **A5.5g** [Jeff] Implement `get_tree(root_plan_id: str) -> list[Plan]`: + - [ ] Use recursive CTE query for all descendants: + ```python + from sqlalchemy import text + cte = text(''' + WITH RECURSIVE plan_tree AS ( + SELECT * FROM lifecycle_plans WHERE plan_id = :root_id + UNION ALL + SELECT lp.* FROM lifecycle_plans lp + INNER JOIN plan_tree pt ON lp.parent_plan_id = pt.plan_id + ) + SELECT * FROM plan_tree ORDER BY created_at ASC + ''') + ``` + - [ ] Execute and map results to domain models + - [ ] Return in tree order (parent before children by creation time) + - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.get_tree()" + - [ ] **A5.5h** [Jeff] Implement `update(plan: Plan) -> Plan`: + - [ ] Fetch existing record by plan_id + - [ ] Raise `PlanNotFoundError` if not exists + - [ ] Update all fields from domain model (use a helper to copy attributes) + - [ ] Auto-update `updated_at` timestamp to now + - [ ] Commit transaction + - [ ] Return updated plan (re-query to ensure consistency) + - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.update()" + - [ ] **A5.5i** [Jeff] Implement `list_all(limit: int = 100, offset: int = 0) -> list[Plan]`: + - [ ] Query all with pagination: `.offset(offset).limit(limit)` + - [ ] Order by created_at DESC + - [ ] Convert to domain models + - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.list_all()" + - [ ] **A5.5j** [Jeff] Implement `count(phase: PlanPhase | None = None, state: ProcessingState | None = None) -> int`: + - [ ] Use `session.query(func.count(LifecyclePlanModel.plan_id))` + - [ ] Apply optional phase filter + - [ ] Apply optional state filter + - [ ] Return `.scalar()` result + - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.count()" + - [ ] **A5.5k** [Jeff] Add `@retry_database` decorator to all methods: + - [ ] Import from `src/cleveragents/core/retry_patterns.py` + - [ ] Configure: 3 retries, exponential backoff (1s, 2s, 4s) + - [ ] Only retry on `OperationalError` (database locked, connection timeout) + - [ ] Do NOT retry on `IntegrityError` (these are application logic errors) + - [ ] Commit: "feat(repo): add retry decorator to LifecyclePlanRepository" + - [ ] **A5.6** [Luis] Implement `ActionRepository` in `src/cleveragents/infrastructure/database/repositories.py`: + - [ ] **A5.6a** [Luis] Define class with session factory injection: + - [ ] Create class `ActionRepository` + - [ ] Add `__init__(self, session_factory: Callable[[], Session])` + - [ ] Commit: "feat(repo): add ActionRepository scaffold" + - [ ] **A5.6b** [Luis] Implement `create(action: Action) -> Action`: + - [ ] Same pattern as LifecyclePlanRepository + - [ ] Handle duplicate name error specifically + - [ ] Commit: "feat(repo): implement ActionRepository.create()" + - [ ] **A5.6c** [Luis] Implement `get_by_id(action_id: str) -> Action | None`: + - [ ] Query by primary key + - [ ] Convert to domain or return None + - [ ] Commit: "feat(repo): implement ActionRepository.get_by_id()" + - [ ] **A5.6d** [Luis] Implement `get_by_name(name: str) -> Action | None`: + - [ ] Query by exact namespaced name match: `.filter_by(name=name).first()` + - [ ] Used for `agents [--data-dir PATH] [--config-path PATH] action show local/my-action` + - [ ] Commit: "feat(repo): implement ActionRepository.get_by_name()" + - [ ] **A5.6e** [Luis] Implement `get_by_namespace(namespace: str, state: ActionState | None = None) -> list[Action]`: + - [ ] Filter by namespace column + - [ ] Optionally filter by state + - [ ] Order by short_name ASC for consistent display + - [ ] Convert all to domain models + - [ ] Commit: "feat(repo): implement ActionRepository.get_by_namespace()" + - [ ] **A5.6f** [Luis] Implement `get_by_state(state: ActionState) -> list[Action]`: + - [ ] Filter by state column + - [ ] Order by updated_at DESC (most recently modified first) + - [ ] Commit: "feat(repo): implement ActionRepository.get_by_state()" + - [ ] **A5.6g** [Luis] Implement `update(action: Action) -> Action`: + - [ ] Fetch by action_id + - [ ] Update all fields + - [ ] Auto-update updated_at + - [ ] Commit and return + - [ ] Commit: "feat(repo): implement ActionRepository.update()" + - [ ] **A5.6h** [Luis] Implement `list_available(namespace: str | None = None) -> list[Action]`: + - [ ] Filter by state='available' + - [ ] Optionally filter by namespace + - [ ] Order by namespace ASC, short_name ASC + - [ ] Used for `agents [--data-dir PATH] [--config-path PATH] action list` + - [ ] Commit: "feat(repo): implement ActionRepository.list_available()" + - [ ] **A5.6i** [Luis] Implement `delete(action_id: str) -> bool`: + - [ ] First check if any plans reference this action: `plan_repo.count(action_id=action_id)` + - [ ] If plans exist, raise `ActionInUseError` with count of plans + - [ ] Otherwise delete the action + - [ ] Return True if deleted + - [ ] Commit: "feat(repo): implement ActionRepository.delete()" + - [ ] **A5.6j** [Luis] Add retry decorator to all methods: + - [ ] Same pattern as LifecyclePlanRepository + - [ ] Commit: "feat(repo): add retry decorator to ActionRepository" + - [ ] **A5.7** [Jeff] Update `PlanLifecycleService` to use repositories: + - [ ] **A5.7a** [Jeff] Modify `__init__()` to accept repository dependencies: + - [ ] Change signature: `def __init__(self, plan_repository: LifecyclePlanRepository, action_repository: ActionRepository):` + - [ ] Store as instance variables: `self._plan_repo = plan_repository`, `self._action_repo = action_repository` + - [ ] REMOVE the in-memory storage: Delete `self._plans: dict` and `self._actions: dict` + - [ ] Update docstring to reflect dependency injection + - [ ] Commit: "refactor(service): update PlanLifecycleService to inject repositories" + - [ ] **A5.7b** [Jeff] Update `create_action()` to use ActionRepository: + - [ ] Replace `self._actions[action.action_id] = action` with `self._action_repo.create(action)` + - [ ] Handle `DuplicateActionError` by converting to user-friendly error message + - [ ] Return the created action from repository (may have database-modified fields) + - [ ] Commit: "refactor(service): update create_action() to use repository" + - [ ] **A5.7c** [Jeff] Update `get_action()` to use repository: + - [ ] Replace dict lookup with `self._action_repo.get_by_id()` or `get_by_name()` + - [ ] Handle both ID and namespaced name lookups + - [ ] Commit: "refactor(service): update get_action() to use repository" + - [ ] **A5.7d** [Jeff] Update `list_actions()` to use repository: + - [ ] Replace dict.values() iteration with `self._action_repo.list_available()` + - [ ] Add namespace filter parameter + - [ ] Add state filter parameter + - [ ] Commit: "refactor(service): update list_actions() to use repository" + - [ ] **A5.7e** [Jeff] Update `use_action()` to use both repositories: + - [ ] Fetch action from ActionRepository by name + - [ ] Raise `ActionNotFoundError` if not exists + - [ ] Raise `ActionNotAvailableError` if action.state != AVAILABLE + - [ ] Create new Plan domain object with ULID, set action_id reference + - [ ] Persist plan via LifecyclePlanRepository.create() + - [ ] Return the created plan + - [ ] Commit: "refactor(service): update use_action() to use repositories" + - [ ] **A5.7f** [Jeff] Update all plan state transition methods: + - [ ] `start_strategize()`: fetch plan → verify phase → update state → save + - [ ] `complete_strategize()`: fetch → verify → update phase+state → save + - [ ] `fail_strategize()`: fetch → update state to ERRORED → set error_message → save + - [ ] `start_execute()`: same pattern + - [ ] `complete_execute()`: same pattern, store changeset_id + - [ ] `fail_execute()`: same pattern + - [ ] `apply_plan()`: verify Execute phase complete → update to APPLIED → set completed_at → save + - [ ] All methods must re-fetch after save to return current state + - [ ] Commit: "refactor(service): update phase transition methods to use repository" + - [ ] **A5.7g** [Jeff] Update `cancel_plan()` to use repository: + - [ ] Fetch plan + - [ ] Verify not in terminal state (APPLIED or CANCELLED) + - [ ] Set state to CANCELLED, set completed_at + - [ ] Persist via repository + - [ ] Commit: "refactor(service): update cancel_plan() to use repository" + - [ ] **A5.7h** [Jeff] Add transaction handling for multi-step operations: + - [ ] For operations that modify multiple entities (e.g., use_action creates plan + may update action): + - [ ] Use UnitOfWork pattern: start transaction, do all operations, commit atomically + - [ ] If any step fails, rollback all changes + - [ ] Create `UnitOfWork` class if not exists: manages session lifecycle + - [ ] Commit: "feat(service): add transaction handling for multi-step operations" + - [ ] **A5.8** [Luis] Update DI container in `src/cleveragents/application/container.py`: + - [ ] **A5.8a** [Luis] Add `LifecyclePlanRepository` provider: + - [ ] Create factory function that instantiates repository with session factory + - [ ] Register with container + - [ ] Ensure proper scoping (singleton or per-request based on usage pattern) + - [ ] Commit: "feat(di): add LifecyclePlanRepository provider" + - [ ] **A5.8b** [Luis] Add `ActionRepository` provider: + - [ ] Same pattern as plan repository + - [ ] Commit: "feat(di): add ActionRepository provider" + - [ ] **A5.8c** [Luis] Update `PlanLifecycleService` provider to inject repositories: + - [ ] Modify service factory to resolve both repositories + - [ ] Pass to PlanLifecycleService constructor + - [ ] Verify dependency chain is correct + - [ ] Commit: "feat(di): update PlanLifecycleService provider with repositories" + - [ ] Tests: Integration tests for plan/action persistence + - [ ] **A5.9** [Rui] Write Behave scenarios in `features/plan_persistence.feature`: + - [ ] **A5.9a** [Rui] Scenario: Create plan stores record in database + - [ ] Given: An action "local/test-action" exists in database with state=AVAILABLE + - [ ] And: A project "local/test-project" exists + - [ ] When: I call `plan_service.use_action("local/test-action", project_ids=["proj-123"])` + - [ ] Then: A plan record exists in the lifecycle_plans table + - [ ] And: The plan_id is a valid 26-character ULID + - [ ] And: The plan.phase is STRATEGIZE + - [ ] And: The plan.state is QUEUED + - [ ] And: The plan.action_id matches the action + - [ ] Commit: "test(behave): add plan creation persistence scenario" + - [ ] **A5.9b** [Rui] Scenario: Update plan phase persists correctly + - [ ] Given: A plan exists in database with phase=STRATEGIZE, state=QUEUED + - [ ] When: I call `plan_service.complete_strategize(plan_id, strategy_context={...})` + - [ ] And: I call `plan_service.start_execute(plan_id)` + - [ ] Then: The database record shows phase='EXECUTE' + - [ ] And: The database record shows state='PROCESSING' + - [ ] And: The updated_at timestamp has changed + - [ ] Commit: "test(behave): add plan phase update persistence scenario" + - [ ] **A5.9c** [Rui] Scenario: Query plans by phase returns filtered results + - [ ] Given: 3 plans exist: 1 in STRATEGIZE, 1 in EXECUTE, 1 in APPLIED + - [ ] When: I query `plan_repo.get_by_phase(PlanPhase.STRATEGIZE)` + - [ ] Then: Only 1 plan is returned + - [ ] And: Its phase is STRATEGIZE + - [ ] Commit: "test(behave): add plan phase query scenario" + - [ ] **A5.9d** [Rui] Scenario: Query plans by state returns filtered results + - [ ] Given: 3 plans exist: 1 QUEUED, 1 PROCESSING, 1 ERRORED + - [ ] When: I query `plan_repo.get_by_state(ProcessingState.ERRORED)` + - [ ] Then: Only 1 plan is returned + - [ ] And: Its state is ERRORED + - [ ] Commit: "test(behave): add plan state query scenario" + - [ ] **A5.9e** [Rui] Scenario: Get plan tree returns parent and all children + - [ ] Given: A root plan exists with plan_id="root-123" + - [ ] And: A child plan exists with parent_plan_id="root-123" + - [ ] And: A grandchild plan exists with parent_plan_id=child_plan_id + - [ ] When: I query `plan_repo.get_tree("root-123")` + - [ ] Then: 3 plans are returned in order + - [ ] And: First plan is the root + - [ ] And: Second plan is the child + - [ ] And: Third plan is the grandchild + - [ ] Commit: "test(behave): add plan tree query scenario" + - [ ] **A5.9f** [Rui] Scenario: Concurrent plan creation is thread-safe + - [ ] Given: An action exists + - [ ] When: 10 threads simultaneously call `plan_service.use_action()` + - [ ] Then: All 10 plans are created successfully + - [ ] And: All 10 plan_ids are unique + - [ ] And: No database integrity errors occurred + - [ ] Commit: "test(behave): add concurrent plan creation scenario" + - [ ] **A5.10** [Rui] Write Behave scenarios in `features/action_persistence.feature`: + - [ ] **A5.10a** [Rui] Scenario: Create action stores record in database + - [ ] Given: No action named "local/test-action" exists + - [ ] When: I call `action_service.create_action()` with valid parameters + - [ ] Then: An action record exists in the actions table + - [ ] And: The action_id is a valid 26-character ULID + - [ ] And: The namespace column is "local" + - [ ] And: The short_name column is "test-action" + - [ ] Commit: "test(behave): add action creation persistence scenario" + - [ ] **A5.10b** [Rui] Scenario: Get action by namespaced name works + - [ ] Given: An action "local/my-action" exists in database + - [ ] When: I call `action_repo.get_by_name("local/my-action")` + - [ ] Then: The action is returned + - [ ] And: Its name matches "local/my-action" + - [ ] Commit: "test(behave): add action name lookup scenario" + - [ ] **A5.10c** [Rui] Scenario: List available excludes archived actions + - [ ] Given: 3 actions exist: 2 with state=AVAILABLE, 1 with state=ARCHIVED + - [ ] When: I call `action_repo.list_available()` + - [ ] Then: Only 2 actions are returned + - [ ] And: Neither has state=ARCHIVED + - [ ] Commit: "test(behave): add action list available scenario" + - [ ] **A5.10d** [Rui] Scenario: Update action state persists + - [ ] Given: An action exists with state=DRAFT + - [ ] When: I call `action_service.make_available(action_id)` + - [ ] Then: The database record shows state='AVAILABLE' + - [ ] Commit: "test(behave): add action state update scenario" + - [ ] **A5.10e** [Rui] Scenario: Delete action with existing plans fails + - [ ] Given: An action "local/used-action" exists + - [ ] And: A plan exists that references this action + - [ ] When: I call `action_repo.delete(action_id)` + - [ ] Then: An ActionInUseError is raised + - [ ] And: The action still exists in the database + - [ ] Commit: "test(behave): add action delete protection scenario" + - [ ] **A5.11** [Rui] Write Robot test `robot/plan_persistence_e2e.robot`: + - [ ] **A5.11a** [Rui] Test: Full lifecycle persists all transitions + - [ ] Create action via CLI: `agents [--data-dir PATH] [--config-path PATH] action create --name local/e2e-test ...` + - [ ] Make action available: `agents [--data-dir PATH] [--config-path PATH] action available ` + - [ ] Create project: `agents [--data-dir PATH] [--config-path PATH] project create --name local/e2e-project` + - [ ] Use action on project: `agents [--data-dir PATH] [--config-path PATH] plan use local/e2e-test --project local/e2e-project` + - [ ] Execute plan: `agents [--data-dir PATH] [--config-path PATH] plan execute ` + - [ ] Apply plan: `agents [--data-dir PATH] [--config-path PATH] plan apply ` + - [ ] Verify via `agents [--data-dir PATH] [--config-path PATH] plan status ` shows APPLIED phase + - [ ] Query database directly to verify all state transitions recorded + - [ ] Commit: "test(robot): add full lifecycle persistence e2e test" + - [ ] **A5.11b** [Rui] Test: Restart persistence + - [ ] Create plan via CLI + - [ ] Get plan_id from output + - [ ] Simulate process crash (kill the process or restart CLI) + - [ ] Run new CLI command: `agents [--data-dir PATH] [--config-path PATH] plan status ` + - [ ] Verify plan still exists and shows correct state + - [ ] Commit: "test(robot): add restart persistence e2e test" + - [ ] **A5.11c** [Rui] Test: Concurrent CLI access + - [ ] Start two CLI processes accessing same plan + - [ ] One process starts execute, other queries status + - [ ] Verify no data corruption or deadlocks + - [ ] Both processes complete successfully + - [ ] Commit: "test(robot): add concurrent CLI access e2e test" -- [ ] **COMMIT (Owner: Rui | Group: A5.tests) - Commit message: "test(persistence): add plan/action persistence suites"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Tests (Behave) [Rui]: Add plan persistence scenarios (create, update phase/state, list filters, plan tree, concurrency). - - [ ] Tests (Behave) [Rui]: Add action persistence scenarios (create, list available, archive guard, action arguments persisted). - - [ ] Tests (Robot) [Rui]: Add plan persistence E2E (full lifecycle, restart persistence, concurrent CLI access). - - [ ] Docs [Rui]: Update `docs/development/testing.md` with persistence suites and `nox` commands. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/persistence_suites_bench.py` for DB read/write baselines. - - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "test(persistence): add plan/action persistence suites"`. +- [ ] **Stage A6: Automation Levels Foundation** (Day 4-5) **[Luis]** + - [ ] Code: Implement basic automation level support + - [ ] **A6.1** [Luis] Add `AutomationLevel` enum to `src/cleveragents/domain/models/core/plan.py`: + - [ ] Value `MANUAL` - user triggers each phase transition + - [ ] Value `REVIEW_BEFORE_APPLY` - auto strategize+execute, pause before apply + - [ ] Value `FULL_AUTOMATION` - all phases automatic + - [ ] **A6.2** [Luis] Add automation level configuration to `src/cleveragents/config/settings.py`: + - [ ] Add `default_automation_level: AutomationLevel` setting + - [ ] Add `CLEVERAGENTS_AUTOMATION_LEVEL` environment variable + - [ ] Implement hierarchy: plan-level > session-level > global-level + - [ ] **A6.3** [Luis] Update `PlanLifecycleService` to respect automation levels: + - [ ] Add `automation_level` parameter to `use_action()` method + - [ ] If automation allows, automatically call `execute_plan()` after strategize completes + - [ ] If full automation, automatically call `apply_plan()` after execute completes + - [ ] Add pause/resume capability for review-before-apply mode + - [ ] **A6.4** [Luis] Update CLI commands to support automation levels: + - [ ] Add `--automation-level` flag to `agents [--data-dir PATH] [--config-path PATH] plan use` command + - [ ] Add `agents [--data-dir PATH] [--config-path PATH] config set automation-level ` command + - [ ] Add `agents [--data-dir PATH] [--config-path PATH] plan set-automation-level ` command: + - [ ] Can change automation level for existing plan + - [ ] Only affects future phase transitions + - [ ] Subplans created after change use new level + - [ ] Add `agents [--data-dir PATH] [--config-path PATH] session set automation-level ` command: + - [ ] Set session-level automation (overrides global) + - [ ] Persists for current session only + - [ ] Tests: Automation level tests + - [ ] **A6.5** [Rui] Write Behave scenarios in `features/automation_levels.feature`: + - [ ] Scenario: Manual mode requires explicit execute command + - [ ] Scenario: Review-before-apply auto-executes but pauses at apply + - [ ] Scenario: Full automation runs all phases without user input + - [ ] Scenario: Plan-level automation overrides global setting + - [ ] Scenario: Change automation level mid-plan works correctly -**Parallel Group A5.legacy: Remove legacy plan build/apply path (M1-critical)** -- [ ] **COMMIT (Owner: Jeff | Group: A5.legacy) - Commit message: "refactor(plan): remove legacy plan service and CLI"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Remove `PlanService` usage from CLI (`plan tell/build/apply/new/current/list/cd/continue`) and delete command handlers from `src/cleveragents/cli/commands/plan.py`. - - [ ] Code [Jeff]: Remove or quarantine legacy `plan_service.py`, `plan_legacy.py`, and legacy CLI helpers; add explicit NotImplementedError where needed. - - [ ] Code [Jeff]: Remove `PlanService` wiring from `src/cleveragents/application/container.py` and any references from `cli/commands/auto_debug.py`. - - [ ] Code [Jeff]: Rename `plan lifecycle-apply` to `plan apply` and update command wiring once legacy apply is removed. - - [ ] Code [Jeff]: Remove or archive legacy `PlanModel`/`PlanStatus` DB tables if unused by v3; document migration path. - - [ ] Docs [Jeff]: Update CLI docs to list only v3 lifecycle commands and new `plan use/execute/apply` flows. - - [ ] Tests (Behave) [Jeff]: Remove/replace legacy scenarios with v3 equivalents and adjust coverage expectations. - - [ ] Tests (Robot) [Jeff]: Remove legacy robot suites and add v3 replacements where needed. - - [ ] Tests (ASV) [Jeff]: Update asv suite to remove legacy plan benchmarks and add v3 lifecycle baseline benchmark. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "refactor(plan): remove legacy plan service and CLI"`. - -**Parallel Group A6: Automation Profiles Foundation [Jeff + Luis]** (M1-critical; depends on A5 persistence) - **PARALLEL SUBTRACK A6.core [Jeff]**: Profile model + built-ins + schema - **PARALLEL SUBTRACK A6.service [Luis]**: Profile resolution + precedence - **PARALLEL SUBTRACK A6.cli [Rui]**: CLI commands for profiles - **SEQUENTIAL MERGE NOTE**: A6.core must land before A6.service/cli; A6.service must land before gating integration in Section 6. -- [ ] **COMMIT (Owner: Jeff | Group: A6.core) - Commit message: "feat(domain): add automation profile model and built-ins"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add `AutomationProfile` model with threshold fields (phase transitions, decision autonomy, child plan spawn, self-repair, apply gating) and validation for 0.0-1.0 ranges. - - [ ] Code [Jeff]: Add built-in profiles (`manual`, `review`, `supervised`, `full-auto`, etc.) per spec with constant definitions and stable IDs. - - [ ] Code [Jeff]: Add YAML schema for automation profiles under `docs/schema/automation_profile.schema.yaml` and loader helper. - - [ ] Docs [Jeff]: Add `docs/reference/automation_profiles.md` describing built-ins and threshold semantics. - - [ ] Tests (Behave) [Jeff]: Add scenarios for profile validation and built-in defaults. - - [ ] Tests (Robot) [Jeff]: Add Robot test that loads each built-in profile and prints summary. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/automation_profile_bench.py` for profile validation. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(domain): add automation profile model and built-ins"`. -- [ ] **COMMIT (Owner: Luis | Group: A6.service) - Commit message: "feat(service): resolve automation profiles with precedence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add `AutomationProfileService` to resolve profiles with precedence (plan > action > project > global). - - [ ] Code [Luis]: Add persistence table `automation_profiles` (namespaced name PK) and repository with list/show/update. - - [ ] Code [Luis]: Add config key `core.automation_profile` and env var override for global default. - - [ ] Docs [Luis]: Update `docs/reference/config.md` with automation profile defaults and override behavior. - - [ ] Tests (Behave) [Luis]: Add scenarios for precedence resolution and missing profile errors. - - [ ] Tests (Robot) [Luis]: Add Robot config smoke test for global profile override. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/automation_profile_resolution_bench.py` for resolution latency. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(service): resolve automation profiles with precedence"`. -- [ ] **COMMIT (Owner: Rui | Group: A6.cli) - Commit message: "feat(cli): add automation-profile commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Rui]: Implement `agents automation-profile add/remove/list/show` commands with YAML config input. - - [ ] Code [Rui]: Add `--automation-profile` to `plan use` and output profile in `plan status`. - - [ ] Docs [Rui]: Update CLI reference with automation-profile command examples. - - [ ] Tests (Behave) [Rui]: Add CLI scenarios for profile add/list/show/remove. - - [ ] Tests (Robot) [Rui]: Add Robot CLI tests for automation-profile commands. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_cli_bench.py` for CLI parsing. - - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "feat(cli): add automation-profile commands"`. - -**M1 SUCCESS CRITERIA (Day 7 MVP - source code only)**: -- Action created from YAML config and persisted (namespaced name, invariants, automation profile). -- Project created and linked to a local git-checkout resource. -- Plan use -> strategize -> execute -> apply completes with sandbox isolation and diff review. -- Tool-based change tracking produces a ChangeSet and applies to the repo after approval. -- `nox` passes with coverage >=97% on the MVP end-to-end path. +**M1 SUCCESS CRITERIA**: +- [ ] Can create an action via CLI and it persists to database +- [ ] Can use an action on a project to create a plan +- [ ] Plan transitions through phases with database persistence +- [ ] Automation levels work (at least manual mode fully functional) --- ### Section 4: Projects & Resources [WORKSTREAM B - Hamza Lead] **Target: Milestone M2 (+10 days)** -**Week 1-2 focus**: local source code only (git-checkout + fs-directory). Database, API, and remote resources are schema-only stubs for future work. -**Parallel Group B1: Resource Registry Core [Hamza + Jeff]** (can start after A5.alpha migrations are available) - **SEQUENTIAL NOTE**: B1 domain models can start immediately; B1 DB migrations must rebase on the latest Alembic head after A5.alpha to keep a linear migration chain. -- [ ] **COMMIT (Owner: Hamza | Group: B1.core) - Commit message: "feat(domain): add resource type spec and resource model"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Create `src/cleveragents/domain/models/core/resource_type.py` with `ResourceTypeSpec`, `ResourceTypeArgument`, `ResourceKind` (physical/virtual), and `SandboxStrategy` enum. - - [ ] Code [Hamza]: Add resource type fields: `user_addable`, `allowed_parents`, `allowed_children`, `auto_discover`, `handler` reference, and `sandbox_strategy` default. - - [ ] Code [Hamza]: Add `Resource` and `ResourceRef` models with ULID, optional namespaced name, type name, location, description, sandbox strategy, read_only, and metadata. - - [ ] Code [Hamza]: Add validators for namespaced naming, ULID format, and parent/child DAG sanity (no self loops, no duplicate edges, type compatibility). - - [ ] Code [Hamza]: Add `docs/schema/resource_type.schema.yaml` with CLI argument definitions, parent/child constraints, and handler metadata. - - [ ] Code [Hamza]: Add resource type YAML loader in `src/cleveragents/resource/schema.py` with version guard and clear error messages. - - [ ] Docs [Hamza]: Add `docs/reference/resource_model.md` with examples for git-checkout and fs-directory resources plus physical/virtual notes. - - [ ] Tests (Behave) [Hamza]: Add scenarios validating ULID format, namespace rules, allowed parent/child type checks, and sandbox strategy defaults. - - [ ] Tests (Robot) [Hamza]: Add Robot test that loads a ResourceTypeSpec YAML fixture and validates it. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/resource_model_bench.py` for resource validation + DAG checks. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(domain): add resource type spec and resource model"`. -- [ ] **COMMIT (Owner: Hamza | Group: B1.core) - Commit message: "feat(resource): add built-in resource type configs"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Add built-in resource type YAML configs under `resources/types/` (git-checkout, fs-directory, fs-file) with sandbox strategy defaults and CLI argument specs. - - [ ] Code [Hamza]: Add bootstrap registration in `ResourceRegistryService` (register built-ins on startup if missing; idempotent). - - [ ] Code [Hamza]: Add mapping table from built-in type to handler/sandbox strategy and surface in `resource type list` output. - - [ ] Docs [Hamza]: Add `docs/reference/resource_types_builtin.md` with per-type flags and examples. - - [ ] Tests (Behave) [Hamza]: Add scenarios ensuring built-in types exist and register idempotently. - - [ ] Tests (Robot) [Hamza]: Add Robot test that lists resource types and asserts built-ins are present. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/resource_type_bootstrap_bench.py` for registration overhead. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(resource): add built-in resource type configs"`. -- [ ] **COMMIT (Owner: Hamza | Group: B1.core) - Commit message: "feat(domain): add project model v3 with linked resources"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Add `Project`, `ProjectResourceLink`, `ProjectValidationSummary` (derived from validation attachments), and `ProjectContextPolicy` models using namespaced name as the unique identifier (no ULID per spec). - - [ ] Code [Hamza]: Add fields for `invariants`, `invariant_actor`, `automation_profile`, and `context_views` (strategize/execute/apply/default). - - [ ] Code [Hamza]: Add validation for resource link overrides (read_only flags, alias uniqueness, resource existence). - - [ ] Code [Hamza]: Add helpers to compute effective invariants and automation profile (project defaults). - - [ ] Docs [Hamza]: Add `docs/reference/project_model.md` describing resource linking, validation attachments, and context view policies. - - [ ] Tests (Behave) [Hamza]: Add scenarios for project model validation, link overrides, and context view inheritance. - - [ ] Tests (Robot) [Hamza]: Add Robot test that creates a Project object and prints serialized output. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/project_model_bench.py` for serialization/validation performance. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(domain): add project model v3 with linked resources"`. -- [ ] **COMMIT (Owner: Jeff | Group: B1.core) - Commit message: "feat(db): add resource registry tables"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Add Alembic migration for `resource_types`, `resources`, and `resource_edges` tables with naming conventions. - - [ ] Code [Jeff]: Define `resource_types` columns: `name`, `namespace`, `description`, `resource_kind`, `sandbox_strategy`, `user_addable`, `handler_ref`, `args_schema_json`, `allowed_parent_types_json`, `allowed_child_types_json`, `auto_discover_json`, timestamps. - - [ ] Code [Jeff]: Define `resources` columns: ULID PK, `namespaced_name`, `namespace`, `type_name`, `resource_kind`, `location`, `description`, `read_only`, `metadata_json`, `sandbox_strategy`, timestamps. - - [ ] Code [Jeff]: Define `resource_edges` columns: `parent_id`, `child_id`, `created_at`, with uniqueness constraint and FK cascade rules. - - [ ] Code [Jeff]: Add indexes on `resources.namespaced_name`, `resources.namespace`, `resources.type_name`, and `resource_edges.parent_id/child_id`. - - [ ] Docs [Jeff]: Update `docs/reference/database_schema.md` with resource registry tables and constraints. - - [ ] Tests (Behave) [Jeff]: Add migration scenarios verifying tables, indices, and edge uniqueness. - - [ ] Tests (Robot) [Jeff]: Add Robot migration smoke test using `nox -s db_migrate`. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/resource_registry_migration_bench.py` for migration baseline. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(db): add resource registry tables"`. +**WEEK 1-2 - PARALLEL WITH PLAN LIFECYCLE** -**Parallel Group B2: Project Persistence + Services [Hamza + Luis]** (depends on B1 domain models) -- [ ] **COMMIT (Owner: Jeff | Group: B2.persistence) - Commit message: "feat(db): add projects and project links tables"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Add Alembic migration skeleton with explicit down_revision to latest A5.alpha head. - - [ ] Code [Jeff]: Define `projects` table with namespaced_name PK, namespace, description, automation_profile, invariant_actor, invariants_json, context_policy_json, tags_json, created_by, timestamps. - - [ ] Code [Jeff]: Add `projects` constraints for non-empty names, namespace/name derivation consistency, and unique namespaced_name. - - [ ] Code [Jeff]: Define `project_resource_links` table with link_id ULID, project_name FK, resource_id FK, alias, read_only, created_at. - - [ ] Code [Jeff]: Add uniqueness constraint on (project_name, resource_id) and index on (project_name, alias) for fast lookups. - - [ ] Code [Jeff]: Add indexes on `project_resource_links.project_name` and `project_resource_links.resource_id` for joins. - - [ ] Docs [Jeff]: Document project table schema and link semantics in `docs/reference/database_schema.md`. - - [ ] Tests (Behave) [Jeff]: Add migration scenarios verifying project tables, FK constraints, and unique link enforcement. - - [ ] Tests (Robot) [Jeff]: Add Robot test that inserts a project and link row and validates alias uniqueness. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/project_migration_bench.py` for migration baseline. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(db): add projects and project links tables"`. -- [ ] **COMMIT (Owner: Hamza | Group: B2.persistence) - Commit message: "feat(repo): add resource repositories"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement `ResourceTypeRepository` CRUD and `ResourceRepository` CRUD with DAG edge helpers. - - [ ] Code [Hamza]: Add methods for tree traversal, child discovery queries, and name/ULID resolution. - - [ ] Code [Hamza]: Add repository guardrails for preventing cycles and duplicate edges. - - [ ] Docs [Hamza]: Document repository interfaces in `docs/reference/repositories.md`. - - [ ] Tests (Behave) [Hamza]: Add repository scenarios for create/get/list/tree and cycle rejection. - - [ ] Tests (Robot) [Hamza]: Add Robot test exercising tree output ordering. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/resource_repository_bench.py` for tree query performance. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(repo): add resource repositories"`. -- [ ] **COMMIT (Owner: Hamza | Group: B2.persistence) - Commit message: "feat(repo): add project repositories"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement `ProjectRepository` and `ProjectResourceLinkRepository` with namespace filtering and name-based lookup. - - [ ] Code [Hamza]: Add methods to list project context policies and derived validation attachment summaries for linked resources. - - [ ] Docs [Hamza]: Update repository docs with project link examples and validation attachment notes. - - [ ] Tests (Behave) [Hamza]: Add scenarios for project create/link/unlink and validation attachment summaries. - - [ ] Tests (Robot) [Hamza]: Add Robot test that links two resources to one project. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/project_repository_bench.py` for link/unlink performance. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(repo): add project repositories"`. -- [ ] **COMMIT (Owner: Hamza | Group: B2.service) - Commit message: "feat(service): add resource registry service"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement `ResourceRegistryService` for register/remove/show/tree operations with name/ULID resolution. - - [ ] Code [Hamza]: Add auto-discovery hook that delegates to resource handlers (git-checkout for MVP). - - [ ] Code [Hamza]: Add validation that resource type supports parent/child linkage before linking. - - [ ] Docs [Hamza]: Add `docs/reference/resource_registry.md` describing API behavior and error cases. - - [ ] Tests (Behave) [Hamza]: Add scenarios for register/remove/show/tree behavior and auto-discovery. - - [ ] Tests (Robot) [Hamza]: Add Robot test that registers a git-checkout and inspects child count. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/resource_registry_service_bench.py` for register/show performance. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(service): add resource registry service"`. -- [ ] **COMMIT (Owner: Luis | Group: B2.service) - Commit message: "feat(service): add project service v3"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Implement `ProjectService` create/list/show/delete/link/unlink methods using repositories. - - [ ] Code [Luis]: Add validation attachment helpers (read-only listing of validation attachments for linked resources) and context policy setters for project views. - - [ ] Code [Luis]: Enforce read-only resource links and project-level invariant actor defaults. - - [ ] Docs [Luis]: Update `docs/reference/project_service.md` with usage examples and error cases. - - [ ] Tests (Behave) [Luis]: Add scenarios for project create/link/unlink/context policy + validation attachment visibility. - - [ ] Tests (Robot) [Luis]: Add Robot test that creates project and links a resource. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/project_service_bench.py` for link/unlink performance. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(service): add project service v3"`. +``` +WORKSTREAM B PARALLEL STRUCTURE: -**Parallel Group B3: CLI Commands [Rui]** (depends on B2 services) -- [ ] **COMMIT (Owner: Rui | Group: B3.cli) - Commit message: "feat(cli): add resource type commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Rui]: Add `agents resource type add/remove/list/show` commands with YAML config input and schema validation. - - [ ] Code [Rui]: Implement `--update` behavior and error on name conflicts per spec. - - [ ] Code [Rui]: Wire `resource type add` to `ResourceTypeSpec` loader with clear error output and schema version guard. - - [ ] Docs [Rui]: Update CLI reference with resource type examples and expected output columns. - - [ ] Tests (Behave) [Rui]: Add scenarios for resource type lifecycle and invalid schema handling. - - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/resource_type_cli.robot`. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_type_cli_bench.py` for config parsing overhead. - - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "feat(cli): add resource type commands"`. -- [ ] **COMMIT (Owner: Rui | Group: B3.cli) - Commit message: "feat(cli): add resource commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Rui]: Add `agents resource add/remove/list/show/tree` commands with type-specific flags and name/ULID resolution. - - [ ] Code [Rui]: Implement `resource inspect --tree/--file` per spec for resource introspection. - - [ ] Code [Rui]: Add `resource link-child` and `resource unlink-child` commands for DAG maintenance. - - [ ] Docs [Rui]: Update CLI reference with resource examples (git-checkout, fs-directory) and output columns. - - [ ] Tests (Behave) [Rui]: Add scenarios for resource registration, list filters, tree rendering, and link-child constraints. - - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/resource_cli.robot`. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_cli_bench.py` for command parsing and list output. - - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "feat(cli): add resource commands"`. -- [ ] **COMMIT (Owner: Rui | Group: B3.cli) - Commit message: "feat(cli): add project commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Rui]: Add `agents project create/show/list/delete/link-resource/unlink-resource` commands using namespaced project names. - - [ ] Code [Rui]: Add `project context set/show` commands (context views per phase) and ensure output includes linked resources + validation attachments. - - [ ] Docs [Rui]: Update CLI reference with project examples and validation attachment visibility (via `agents validation attach`). - - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/unlink/context policies and validation display. - - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/project_cli.robot`. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_cli_bench.py` for command parsing and list output. - - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "feat(cli): add project commands"`. +TRACK B.alpha [Hamza - Day 1-2]: Domain Models (B1.1-B1.6) + └── Can start immediately, no dependencies + +TRACK B.beta [Hamza - Day 2-3]: CLI Commands (B2.1-B2.2) + └── Depends on B1 models + +TRACK B.gamma [Hamza + Luis - Day 3-5]: Sandbox Framework (B3.1-B3.8) + └── Depends on B1 Resource model + └── Luis owns Protocol (B3.1-B3.2), Hamza owns Implementations (B3.3-B3.4) + +TRACK B.delta [Hamza - Day 5-6]: Resource Service (B4.1-B4.3) + └── Depends on B3 sandbox + +TRACK B.epsilon [Hamza - Day 6-7]: Persistence (B5.1-B5.5) + └── Depends on B1 models, parallel with B4 -**Parallel Group B3.cleanup: Legacy Project Removal [Jeff]** (after B3.cli lands) +TESTING [Rui - Continuous]: Write tests BEFORE implementation +``` -- [ ] **COMMIT (Owner: Jeff | Group: B3.cleanup) - Commit message: "refactor(project): remove legacy project init/status commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Remove legacy `agents project init/status/clean/file-filter` commands from `src/cleveragents/cli/commands/project.py`. - - [ ] Code [Jeff]: Remove `.cleveragents` directory bootstrap logic from legacy `ProjectService` and deprecate `ProjectSettings` fields tied to local init. - - [ ] Code [Jeff]: Update `src/cleveragents/application/container.py` to stop wiring legacy ProjectService once v3 service is in place. - - [ ] Code [Jeff]: Remove legacy `src/cleveragents/domain/models/core/project.py` in favor of v3 project model and update imports. - - [ ] Docs [Jeff]: Remove references to `agents project init` from CLI docs and point to `agents project create` + `agents init` (global) flows. - - [ ] Tests (Behave) [Jeff]: Remove/replace legacy project init scenarios with v3 project create scenarios. - - [ ] Tests (Robot) [Jeff]: Remove legacy project init Robot suites and add v3 replacements if missing. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/project_cli_cleanup_bench.py` for CLI help/rendering baseline after removal. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "refactor(project): remove legacy project init/status commands"`. +- [ ] **Stage B1: Project Data Model** (Day 1-2) **[Hamza - Python Expert, RDF Background]** + + **SEQUENTIAL ORDER**: B1.3 (ResourceType) → B1.4 (SandboxStrategy) → B1.2 (Resource) → B1.5 (ValidationConfig) → B1.6 (ContextConfig) → B1.1 (Project) + The order matters because Project depends on Resource, which depends on enums. + + - [ ] Code: Create Project domain model + - [ ] **B1.3** [Hamza] Define `ResourceType` enum in `src/cleveragents/domain/models/core/resource.py`: + - [ ] **B1.3a** [Hamza] Create the file with proper imports: + - [ ] Import `Enum` from enum module + - [ ] Import `str` for string mixin: `class ResourceType(str, Enum):` + - [ ] Add module docstring explaining resource types + - [ ] Commit: "feat(domain): create resource.py with ResourceType enum scaffold" + - [ ] **B1.3b** [Hamza] Define enum values with descriptive docstrings: + - [ ] `GIT_REPOSITORY = "git_repository"` - Git repo (local path or remote URL), supports worktree sandboxing + - [ ] `FILESYSTEM = "filesystem"` - Local directory or file tree, supports copy-on-write sandboxing + - [ ] `DATABASE = "database"` - SQL/NoSQL database endpoint, supports transaction-based sandboxing + - [ ] `API_ENDPOINT = "api_endpoint"` - REST/GraphQL API, typically cannot be sandboxed + - [ ] `DOCUMENT_CORPUS = "document_corpus"` - Collection of documents (PDFs, markdown, wikis) + - [ ] `CLOUD_INFRASTRUCTURE = "cloud_infrastructure"` - Cloud resources (AWS, GCP, Azure) + - [ ] Commit: "feat(domain): define ResourceType enum values" + - [ ] **B1.4** [Hamza] Define `SandboxStrategy` enum in same file: + - [ ] **B1.4a** [Hamza] Define enum with string values: + - [ ] `GIT_WORKTREE = "git_worktree"` - Use `git worktree add` for isolation, efficient for git repos + - [ ] `COPY_ON_WRITE = "copy_on_write"` - Copy directory to temp location, universal but can be slow for large dirs + - [ ] `OVERLAY = "overlay"` - Use overlayfs (Linux only), efficient copy-on-write for large directories + - [ ] `TRANSACTION_ROLLBACK = "transaction_rollback"` - Database transaction that can be rolled back + - [ ] `VERSIONING = "versioning"` - Use versioning features (e.g., S3 versioning) + - [ ] `NONE = "none"` - No sandboxing possible, modifications are immediate and irreversible + - [ ] Commit: "feat(domain): define SandboxStrategy enum values" + - [ ] **B1.4b** [Hamza] Add helper method to check sandbox capabilities: + - [ ] `@classmethod def supports_rollback(cls, strategy: 'SandboxStrategy') -> bool:` - Returns True for all except NONE + - [ ] `@classmethod def is_copy_based(cls, strategy: 'SandboxStrategy') -> bool:` - Returns True for COPY_ON_WRITE, OVERLAY + - [ ] Commit: "feat(domain): add SandboxStrategy helper methods" + - [ ] **B1.2** [Hamza] Define `Resource` Pydantic model in `src/cleveragents/domain/models/core/resource.py`: + - [ ] **B1.2a** [Hamza] Create basic model structure: + - [ ] Import `BaseModel, Field, field_validator` from pydantic + - [ ] Import `datetime` for timestamps + - [ ] Import `Any` from typing for metadata dict + - [ ] Create `class Resource(BaseModel):` with `model_config = ConfigDict(frozen=True)` + - [ ] Commit: "feat(domain): add Resource model scaffold" + - [ ] **B1.2b** [Hamza] Define all fields with proper types and descriptions: + - [ ] `resource_id: str = Field(..., description="ULID primary identifier")` - Required, no default + - [ ] `name: str = Field(..., min_length=1, max_length=100, description="Human-readable resource name")` + - [ ] `type: ResourceType = Field(..., description="Type of resource determining available operations")` + - [ ] `location: str = Field(..., description="Path, URL, or connection string")` + - [ ] `is_remote: bool = Field(default=False, description="Whether resource is network-accessible")` + - [ ] `sandbox_strategy: SandboxStrategy = Field(..., description="How to sandbox this resource during execution")` + - [ ] `read_only: bool = Field(default=False, description="If True, write operations are blocked")` + - [ ] `metadata: dict[str, Any] = Field(default_factory=dict, description="Additional type-specific metadata")` + - [ ] `created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation timestamp")` + - [ ] Commit: "feat(domain): define Resource model fields" + - [ ] **B1.2c** [Hamza] Add validators: + - [ ] `@field_validator('resource_id')` - Validate ULID format (26 alphanumeric chars) + - [ ] `@field_validator('location')` - Validate based on type (path for filesystem, URL for git remote, etc.) + - [ ] `@field_validator('sandbox_strategy')` - Warn if incompatible with resource type (e.g., GIT_WORKTREE on FILESYSTEM) + - [ ] Add `@model_validator(mode='after')` to check sandbox_strategy is compatible with type + - [ ] Commit: "feat(domain): add Resource model validators" + - [ ] **B1.2d** [Hamza] Add computed properties and helper methods: + - [ ] `@property def supports_sandbox(self) -> bool:` - Returns True if sandbox_strategy != NONE + - [ ] `@property def can_write(self) -> bool:` - Returns True if not read_only + - [ ] `def get_sandbox_path(self, base_dir: str) -> str:` - Generate sandbox path for this resource + - [ ] Commit: "feat(domain): add Resource helper methods" + - [ ] **B1.5** [Hamza] Define `ValidationConfig` Pydantic model in `src/cleveragents/domain/models/core/project.py`: + - [ ] **B1.5a** [Hamza] Create file with ValidationConfig model: + - [ ] Import necessary Pydantic types + - [ ] Create `class ValidationConfig(BaseModel):` + - [ ] Commit: "feat(domain): create project.py with ValidationConfig scaffold" + - [ ] **B1.5b** [Hamza] Define all fields: + - [ ] `test_command: str | None = Field(default=None, description="Shell command to run tests (e.g., 'pytest')")` + - [ ] `lint_command: str | None = Field(default=None, description="Shell command to run linter (e.g., 'ruff check .')")` + - [ ] `type_check_command: str | None = Field(default=None, description="Shell command for type checking (e.g., 'pyright')")` + - [ ] `build_command: str | None = Field(default=None, description="Shell command to build project (e.g., 'npm run build')")` + - [ ] `custom_commands: dict[str, str] = Field(default_factory=dict, description="Named custom validation commands")` + - [ ] `timeout_seconds: int = Field(default=300, description="Maximum time for each validation command")` + - [ ] `fail_on_lint_error: bool = Field(default=True, description="Whether lint errors should block apply")` + - [ ] Commit: "feat(domain): define ValidationConfig fields" + - [ ] **B1.5c** [Hamza] Add helper methods: + - [ ] `def get_all_commands(self) -> dict[str, str]:` - Returns all non-None commands as dict + - [ ] `def has_any_validation(self) -> bool:` - Returns True if any command is configured + - [ ] Commit: "feat(domain): add ValidationConfig helper methods" + - [ ] **B1.6** [Hamza] Define `ContextConfig` Pydantic model: + - [ ] **B1.6a** [Hamza] Define all fields: + - [ ] `ignore_patterns: list[str] = Field(default_factory=list, description="Gitignore-style patterns to exclude from indexing")` + - [ ] `include_patterns: list[str] | None = Field(default=None, description="If set, only files matching these patterns are included")` + - [ ] `max_file_size: int = Field(default=1_000_000, description="Maximum file size in bytes to index (default 1MB)")` + - [ ] `max_files: int = Field(default=100_000, description="Maximum number of files to index")` + - [ ] `indexing_strategy: str = Field(default="full_text", description="How to index: full_text, embeddings, or both")` + - [ ] `chunking_policy: str = Field(default="smart", description="How to chunk large files: fixed, semantic, or smart")` + - [ ] `chunk_size: int = Field(default=1000, description="Target chunk size in tokens for chunking")` + - [ ] Commit: "feat(domain): define ContextConfig fields" + - [ ] **B1.6b** [Hamza] Add default ignore patterns: + - [ ] `@field_validator('ignore_patterns', mode='before')` - Merge with defaults if not explicitly empty + - [ ] Default patterns: `[".git/", "node_modules/", "__pycache__/", ".venv/", "*.pyc", ".DS_Store"]` + - [ ] Commit: "feat(domain): add ContextConfig default ignore patterns" + - [ ] **B1.1** [Hamza] Define `Project` Pydantic model in `src/cleveragents/domain/models/core/project.py`: + - [ ] **B1.1a** [Hamza] Import Resource model and create Project class: + - [ ] Import `Resource` from resource module + - [ ] Import `ValidationConfig`, `ContextConfig` from same file + - [ ] Create `class Project(BaseModel):` with proper config + - [ ] Commit: "feat(domain): add Project model scaffold" + - [ ] **B1.1b** [Hamza] Define identity fields: + - [ ] `project_id: str = Field(..., description="ULID primary identifier")` + - [ ] `name: str = Field(..., min_length=1, max_length=100, description="Project display name")` + - [ ] `namespace: str = Field(default="local", description="Namespace: local/, username/, orgname/")` + - [ ] `description: str | None = Field(default=None, max_length=500, description="Optional project description")` + - [ ] Commit: "feat(domain): define Project identity fields" + - [ ] **B1.1c** [Hamza] Define categorization and resource fields: + - [ ] `tags: list[str] = Field(default_factory=list, description="Categorization tags (e.g., 'python', 'backend')")` + - [ ] `resources: list[Resource] = Field(default_factory=list, description="Resources associated with this project")` + - [ ] `validation_config: ValidationConfig | None = Field(default=None, description="Project-level validation commands")` + - [ ] `context_config: ContextConfig = Field(default_factory=ContextConfig, description="Context indexing configuration")` + - [ ] Commit: "feat(domain): define Project categorization and resource fields" + - [ ] **B1.1d** [Hamza] Define timestamp fields: + - [ ] `created_at: datetime = Field(default_factory=datetime.utcnow)` + - [ ] `updated_at: datetime = Field(default_factory=datetime.utcnow)` + - [ ] Commit: "feat(domain): define Project timestamp fields" + - [ ] **B1.1e** [Hamza] Add computed property for is_remote: + - [ ] `@property def is_remote(self) -> bool:` - Returns True only if ALL resources have is_remote=True + - [ ] Empty resources list: return False (local by default) + - [ ] Mixed local/remote: return False (has local resources, so project is local) + - [ ] All remote: return True (can execute on server) + - [ ] Commit: "feat(domain): add Project.is_remote computed property" + - [ ] **B1.1f** [Hamza] Add namespace validator: + - [ ] `@field_validator('namespace')` - Validate namespace format + - [ ] Must match pattern: `^(local|[a-z][a-z0-9_]{0,49})$` (local or valid identifier) + - [ ] Reserved namespaces: `["openai", "anthropic", "google", "cleveragents"]` - reject these + - [ ] Commit: "feat(domain): add Project namespace validator" + - [ ] **B1.1g** [Hamza] Add namespaced_name property and helpers: + - [ ] `@property def namespaced_name(self) -> str:` - Returns f"{self.namespace}/{self.name}" + - [ ] `@classmethod def parse_namespaced_name(cls, full_name: str) -> tuple[str, str]:` - Split into (namespace, name) + - [ ] `def add_resource(self, resource: Resource) -> 'Project':` - Returns new project with resource added (immutable pattern) + - [ ] `def remove_resource(self, resource_id: str) -> 'Project':` - Returns new project without resource + - [ ] `def get_resource(self, name: str) -> Resource | None:` - Find resource by name + - [ ] Commit: "feat(domain): add Project helper methods" + - [ ] Tests: Behave scenarios for model validation + - [ ] **B1.7** [Rui] Write 25 Behave scenarios in `features/project_model.feature`: + - [ ] **B1.7a** [Rui] Project creation scenarios: + - [ ] Scenario: Create valid project with all required fields + - [ ] Scenario: Create project with optional description + - [ ] Scenario: Create project with multiple tags + - [ ] Scenario: Project creation fails with empty name + - [ ] Scenario: Project creation fails with name > 100 chars + - [ ] Commit: "test(behave): add project creation scenarios" + - [ ] **B1.7b** [Rui] Namespace validation scenarios: + - [ ] Scenario: Project namespace "local" is valid + - [ ] Scenario: Project namespace "myuser" is valid + - [ ] Scenario: Project namespace "my_org_name" is valid + - [ ] Scenario: Project namespace starting with number is invalid + - [ ] Scenario: Project namespace "openai" (reserved) is rejected + - [ ] Scenario: Project namespaced_name returns "namespace/name" format + - [ ] Commit: "test(behave): add namespace validation scenarios" + - [ ] **B1.7c** [Rui] is_remote derivation scenarios: + - [ ] Scenario: Project with no resources has is_remote=False + - [ ] Scenario: Project with one local resource has is_remote=False + - [ ] Scenario: Project with one remote resource has is_remote=True + - [ ] Scenario: Project with mixed local/remote resources has is_remote=False + - [ ] Scenario: Project with all remote resources has is_remote=True + - [ ] Commit: "test(behave): add is_remote derivation scenarios" + - [ ] **B1.7d** [Rui] Resource model scenarios: + - [ ] Scenario: Resource with each ResourceType value validates correctly + - [ ] Scenario: Resource with GIT_WORKTREE strategy on GIT_REPOSITORY is valid + - [ ] Scenario: Resource with COPY_ON_WRITE strategy on FILESYSTEM is valid + - [ ] Scenario: Resource with TRANSACTION_ROLLBACK on DATABASE is valid + - [ ] Scenario: Resource with read_only=True rejects write operations + - [ ] Scenario: Resource location validated based on type + - [ ] Commit: "test(behave): add resource model scenarios" + - [ ] **B1.7e** [Rui] ValidationConfig scenarios: + - [ ] Scenario: ValidationConfig with all commands validates + - [ ] Scenario: ValidationConfig with only test_command validates + - [ ] Scenario: ValidationConfig get_all_commands returns non-None commands + - [ ] Scenario: ValidationConfig custom_commands are included + - [ ] Commit: "test(behave): add ValidationConfig scenarios" + - [ ] **B1.7f** [Rui] ContextConfig scenarios: + - [ ] Scenario: ContextConfig ignore patterns accept glob syntax + - [ ] Scenario: ContextConfig default ignore patterns applied + - [ ] Scenario: ContextConfig max_file_size enforced + - [ ] Commit: "test(behave): add ContextConfig scenarios" + - [ ] **B1.7g** [Rui] Serialization scenarios: + - [ ] Scenario: Project JSON serialization round-trips correctly + - [ ] Scenario: Resource JSON serialization preserves enum values + - [ ] Scenario: Project with nested resources serializes completely + - [ ] Commit: "test(behave): add serialization round-trip scenarios" -**Parallel Group B4: Sandboxing [Luis + Jeff]** (depends on resource registry + project links) -- [ ] **COMMIT (Owner: Luis | Group: B4.sandbox) - Commit message: "feat(sandbox): add sandbox strategy interface and manager"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Add `SandboxStrategy` protocol, `SandboxRef`, `SandboxManager`, and `SandboxRegistry` with per-resource sandboxes. - - [ ] Code [Luis]: Implement lazy sandbox creation, cleanup hooks, and plan-scoped retention policy stubs. - - [ ] Code [Luis]: Add sandbox path rewriting helper for tool execution and MCP adapters. - - [ ] Docs [Luis]: Add `docs/reference/sandbox.md` describing lifecycle, APIs, and path rewriting rules. - - [ ] Tests (Behave) [Luis]: Add scenarios for sandbox manager creation, cleanup, and path rewrite behavior. - - [ ] Tests (Robot) [Luis]: Add Robot test that creates a sandbox and verifies filesystem isolation. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/sandbox_manager_bench.py` for sandbox creation overhead. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(sandbox): add sandbox strategy interface and manager"`. -- [ ] **COMMIT (Owner: Luis | Group: B4.sandbox) - Commit message: "feat(sandbox): implement git_worktree strategy"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Implement git worktree creation, checkout, and cleanup for git-checkout resources. - - [ ] Code [Luis]: Add safe fallback for repositories without clean worktrees and clear error messages. - - [ ] Code [Luis]: Record sandbox metadata (worktree path, branch, base commit) for rollback. - - [ ] Docs [Luis]: Update sandbox doc with git_worktree usage and rollback behavior. - - [ ] Tests (Behave) [Luis]: Add scenarios for git worktree sandbox creation and rollback. - - [ ] Tests (Robot) [Luis]: Add Robot test that modifies sandbox and verifies original repo unchanged. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/git_worktree_bench.py` for sandbox creation time. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(sandbox): implement git_worktree strategy"`. -- [ ] **COMMIT (Owner: Hamza | Group: B4.sandbox) - Commit message: "feat(resource): add git-checkout handler and discovery"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Add git-checkout handler that validates repo path, branch, and read_only flags. - - [ ] Code [Hamza]: Implement child resource discovery for fs-directory children (schema-only for now) and record ULID-only children. - - [ ] Code [Hamza]: Add sandbox strategy mapping for git-checkout and path normalization helpers. - - [ ] Docs [Hamza]: Document git-checkout handler behavior in `docs/reference/resources_git.md`. - - [ ] Tests (Behave) [Hamza]: Add scenarios for handler validation and discovery counts. - - [ ] Tests (Robot) [Hamza]: Add Robot test registering a git repo and asserting discovered children. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/git_discovery_bench.py` for discovery cost. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(resource): add git-checkout handler and discovery"`. -- [ ] **COMMIT (Owner: Luis | Group: B4.sandbox) - Commit message: "feat(sandbox): add copy_on_write strategy stub"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Add copy_on_write strategy skeleton with TODOs for large-project optimization. - - [ ] Code [Luis]: Raise explicit NotImplementedError with guidance on when it will be available. - - [ ] Docs [Luis]: Document that copy_on_write is stubbed for post-M1 work. - - [ ] Tests (Behave) [Luis]: Add scenario that selecting copy_on_write raises NotImplementedError with clear message. - - [ ] Tests (Robot) [Luis]: Add Robot test verifying stub error output. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/sandbox_stub_bench.py` (baseline no-op). - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(sandbox): add copy_on_write strategy stub"`. +- [ ] **Stage B2: Project CLI Commands** (Day 3-4) **[Hamza]** + + **SEQUENTIAL ORDER**: B2.1 (File scaffold) → B2.2 (Create) → B2.3 (Add resource) → B2.4 (Remove resource) → B2.5 (List) → B2.6 (Show) → B2.7 (Validation) → B2.8 (Delete) → B2.9 (Register) + + - [ ] Code: Implement project CLI + - [ ] **B2.1** [Hamza] Create `src/cleveragents/cli/commands/project.py` scaffold: + - [ ] **B2.1a** [Hamza] Create file with imports and Click group: + - [ ] Import `click` for CLI framework + - [ ] Import `rich.console.Console`, `rich.table.Table` for output + - [ ] Import Project, Resource models from domain + - [ ] Import ProjectService from application.services + - [ ] Create `@click.group(name="project")` decorator + - [ ] Add docstring: "Manage projects and their resources" + - [ ] Commit: "feat(cli): create project.py with Click group scaffold" + - [ ] **B2.1b** [Hamza] Create ProjectService in `src/cleveragents/application/services/project_service.py`: + - [ ] Import ProjectRepository, ResourceRepository + - [ ] Define `class ProjectService:` + - [ ] Add `__init__(self, project_repo: ProjectRepository, resource_repo: ResourceRepository)` + - [ ] Add stub methods: `create_project()`, `get_project()`, `list_projects()`, `delete_project()` + - [ ] Commit: "feat(service): add ProjectService scaffold" + - [ ] **B2.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project create` command: + - [ ] **B2.2a** [Hamza] Define command signature: + ```python + @project.command("create") + @click.option("--name", "-n", required=True, help="Project name (namespace/name format)") + @click.option("--description", "-d", default=None, help="Project description") + @click.option("--tag", "-t", multiple=True, help="Project tags (can specify multiple)") + def create_project(name: str, description: str | None, tag: tuple[str, ...]): + ``` + - [ ] Commit: "feat(cli): add project create command signature" + - [ ] **B2.2b** [Hamza] Implement namespace parsing: + - [ ] Split name on "/" to get (namespace, short_name) + - [ ] If no "/" present, default namespace to "local" + - [ ] Validate namespace: must match `^(local|[a-z][a-z0-9_]{0,49})$` + - [ ] Validate short_name: 1-100 chars, alphanumeric + hyphens + - [ ] Raise `click.BadParameter` on validation failure + - [ ] Commit: "feat(cli): implement namespace parsing in project create" + - [ ] **B2.2c** [Hamza] Implement project creation: + - [ ] Generate ULID for project_id: `ulid.new().str` + - [ ] Create `Project` domain model with all fields + - [ ] Call `project_service.create_project(project)` + - [ ] Handle `DuplicateProjectError` - display user-friendly message + - [ ] Commit: "feat(cli): implement project creation logic" + - [ ] **B2.2d** [Hamza] Implement success output: + - [ ] Display: "Created project: {namespace}/{short_name}" + - [ ] Display: "Project ID: {project_id}" + - [ ] Display: "Tags: {tags}" if any + - [ ] Use Rich console for colored output (green for success) + - [ ] Commit: "feat(cli): add project create success output" + - [ ] **B2.2e** [Hamza] Add ProjectService.create_project() implementation: + - [ ] Validate project.name is unique via repository + - [ ] If duplicate, raise `DuplicateProjectError(name=project.name)` + - [ ] Persist via `self._project_repo.create(project)` + - [ ] Return created project + - [ ] Commit: "feat(service): implement ProjectService.create_project()" + - [ ] **B2.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project add-resource` command: + - [ ] **B2.3a** [Hamza] Define command signature: + ```python + @project.command("add-resource") + @click.option("--project", "-p", required=True, help="Project name (namespace/name)") + @click.option("--name", "-n", required=True, help="Resource name") + @click.option("--type", "-t", "resource_type", required=True, + type=click.Choice(["git_repository", "filesystem", "database", "api_endpoint"])) + @click.option("--location", "-l", required=True, help="Path, URL, or connection string") + @click.option("--sandbox-strategy", "-s", required=True, + type=click.Choice(["git_worktree", "copy_on_write", "transaction_rollback", "none"])) + @click.option("--read-only", is_flag=True, help="Mark resource as read-only") + @click.option("--metadata", "-m", multiple=True, help="Key=value metadata pairs") + ``` + - [ ] Commit: "feat(cli): add project add-resource command signature" + - [ ] **B2.3b** [Hamza] Implement resource type validation: + - [ ] Map CLI type string to ResourceType enum + - [ ] Validate sandbox strategy is compatible with resource type: + - [ ] git_repository: allows git_worktree, copy_on_write, none + - [ ] filesystem: allows copy_on_write, overlay, none + - [ ] database: allows transaction_rollback, none + - [ ] api_endpoint: only allows none + - [ ] Raise `click.BadParameter` if incompatible + - [ ] Commit: "feat(cli): validate resource type and sandbox strategy compatibility" + - [ ] **B2.3c** [Hamza] Implement location validation: + - [ ] For git_repository: validate path exists or URL is valid git URL + - [ ] For filesystem: validate path exists and is directory + - [ ] For database: validate connection string format (basic check) + - [ ] For api_endpoint: validate URL format + - [ ] Commit: "feat(cli): validate resource location by type" + - [ ] **B2.3d** [Hamza] Implement metadata parsing: + - [ ] Parse each `--metadata` value as "key=value" + - [ ] Build dict from all pairs + - [ ] Handle missing "=" gracefully (error) + - [ ] Commit: "feat(cli): parse metadata key=value pairs" + - [ ] **B2.3e** [Hamza] Implement resource creation and linking: + - [ ] Fetch project by namespaced name + - [ ] Raise `ProjectNotFoundError` if not exists + - [ ] Check resource name is unique within project + - [ ] Create Resource with ULID and all fields + - [ ] Add resource to project: `project_service.add_resource(project_id, resource)` + - [ ] Display success: "Added resource '{name}' to project '{project_name}'" + - [ ] Commit: "feat(cli): implement add-resource creation and linking" + - [ ] **B2.3f** [Hamza] Add ProjectService.add_resource() implementation: + - [ ] Fetch project from repository + - [ ] Check for duplicate resource name in project + - [ ] Create new project with resource added (immutable pattern) + - [ ] Recompute project.is_remote based on all resources + - [ ] Update project in repository + - [ ] Create resource in resource repository + - [ ] Return updated project + - [ ] Commit: "feat(service): implement ProjectService.add_resource()" + - [ ] **B2.4** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project remove-resource` command: + - [ ] **B2.4a** [Hamza] Define command signature: + ```python + @project.command("remove-resource") + @click.option("--project", "-p", required=True, help="Project name") + @click.option("--name", "-n", required=True, help="Resource name to remove") + @click.option("--yes", is_flag=True, help="Skip confirmation") + ``` + - [ ] Commit: "feat(cli): add project remove-resource command signature" + - [ ] **B2.4b** [Hamza] Implement removal logic: + - [ ] Fetch project and validate resource exists + - [ ] If not --yes, prompt for confirmation: "Remove resource '{name}'? [y/N]" + - [ ] Call `project_service.remove_resource(project_id, resource_name)` + - [ ] Display success: "Removed resource '{name}' from project" + - [ ] Commit: "feat(cli): implement remove-resource logic" + - [ ] **B2.4c** [Hamza] Add ProjectService.remove_resource() implementation: + - [ ] Fetch project + - [ ] Find resource by name + - [ ] Raise `ResourceNotFoundError` if not exists + - [ ] Create new project without resource (immutable pattern) + - [ ] Recompute is_remote + - [ ] Update project + - [ ] Delete resource from resource repository + - [ ] Return updated project + - [ ] Commit: "feat(service): implement ProjectService.remove_resource()" + - [ ] **B2.5** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project list` command: + - [ ] **B2.5a** [Hamza] Define command signature: + ```python + @project.command("list") + @click.option("--namespace", "-n", default=None, help="Filter by namespace") + @click.option("--tag", "-t", default=None, help="Filter by tag") + @click.option("--format", "output_format", type=click.Choice(["table", "json"]), default="table") + ``` + - [ ] Commit: "feat(cli): add project list command signature" + - [ ] **B2.5b** [Hamza] Implement query and filtering: + - [ ] Call `project_service.list_projects(namespace=namespace, tag=tag)` + - [ ] If namespace provided, filter by namespace + - [ ] If tag provided, filter projects that have this tag + - [ ] Commit: "feat(cli): implement project list filtering" + - [ ] **B2.5c** [Hamza] Implement table output: + - [ ] Create Rich Table with columns: ID, Name, Resources, Tags, Remote + - [ ] Add row for each project: + - [ ] ID: first 8 chars of project_id + - [ ] Name: namespaced_name + - [ ] Resources: count of resources + - [ ] Tags: comma-separated tags (truncate if >3) + - [ ] Remote: "Yes" or "No" based on is_remote + - [ ] Display table via console.print() + - [ ] If no projects found, display "No projects found" + - [ ] Commit: "feat(cli): implement project list table output" + - [ ] **B2.5d** [Hamza] Implement JSON output: + - [ ] If format=json, serialize projects to JSON + - [ ] Use model_dump_json() for each project + - [ ] Print to stdout (for piping to jq, etc.) + - [ ] Commit: "feat(cli): implement project list JSON output" + - [ ] **B2.5e** [Hamza] Add ProjectService.list_projects() implementation: + - [ ] Call `project_repo.list_all()` + - [ ] Apply namespace filter if provided + - [ ] Apply tag filter if provided + - [ ] Return filtered list + - [ ] Commit: "feat(service): implement ProjectService.list_projects()" + - [ ] **B2.6** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project show` command: + - [ ] **B2.6a** [Hamza] Define command signature: + ```python + @project.command("show") + @click.argument("name") + @click.option("--format", "output_format", type=click.Choice(["rich", "json"]), default="rich") + ``` + - [ ] Commit: "feat(cli): add project show command signature" + - [ ] **B2.6b** [Hamza] Implement project fetch and rich display: + - [ ] Fetch project by namespaced name via service + - [ ] If not found, display error and exit(1) + - [ ] Display project details using Rich panels: + ``` + ╭─ Project: local/my-project ────────────────────────╮ + │ ID: 01ARZ3NDEKTSV4RRFFQ69G5FAV │ + │ Description: My awesome project │ + │ Tags: python, backend │ + │ Remote: No │ + │ Created: 2024-01-15 10:30:00 │ + ╰───────────────────────────────────────────────────╯ + + Resources (2): + ┌─────────────────┬──────────────────┬─────────────────┬──────────┐ + │ Name │ Type │ Location │ Strategy │ + ├─────────────────┼──────────────────┼─────────────────┼──────────┤ + │ source │ git_repository │ /path/to/repo │ worktree │ + │ config │ filesystem │ /path/to/config │ copy │ + └─────────────────┴──────────────────┴─────────────────┴──────────┘ + + Validation Config: + Test: pytest tests/ + Lint: ruff check src/ + Type Check: pyright src/ + ``` + - [ ] Commit: "feat(cli): implement project show rich display" + - [ ] **B2.6c** [Hamza] Implement JSON output: + - [ ] If format=json, output full project as JSON + - [ ] Include all resources and validation config + - [ ] Commit: "feat(cli): implement project show JSON output" + - [ ] **B2.6d** [Hamza] Add ProjectService.get_project() implementation: + - [ ] Parse namespaced name to (namespace, short_name) + - [ ] Query by namespaced_name OR by project_id (support both) + - [ ] Return Project with resources loaded + - [ ] Commit: "feat(service): implement ProjectService.get_project()" + - [ ] **B2.7** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project set-validation` command: + - [ ] **B2.7a** [Hamza] Define command signature: + ```python + @project.command("set-validation") + @click.option("--project", "-p", required=True, help="Project name") + @click.option("--test-command", default=None, help="Command to run tests") + @click.option("--lint-command", default=None, help="Command to run linter") + @click.option("--type-check-command", default=None, help="Command for type checking") + @click.option("--build-command", default=None, help="Command to build project") + @click.option("--timeout", default=300, type=int, help="Timeout for each command (seconds)") + @click.option("--clear", is_flag=True, help="Clear all validation config") + ``` + - [ ] Commit: "feat(cli): add project set-validation command signature" + - [ ] **B2.7b** [Hamza] Implement validation config update: + - [ ] Fetch project + - [ ] If --clear, set validation_config to None + - [ ] Otherwise, create ValidationConfig with provided commands + - [ ] Only set commands that were explicitly provided (preserve existing if not specified) + - [ ] Call `project_service.update_validation(project_id, config)` + - [ ] Display updated config summary + - [ ] Commit: "feat(cli): implement set-validation logic" + - [ ] **B2.7c** [Hamza] Add ProjectService.update_validation() implementation: + - [ ] Fetch project + - [ ] Merge new config with existing (if not --clear) + - [ ] Update project with new validation_config + - [ ] Persist via repository + - [ ] Commit: "feat(service): implement ProjectService.update_validation()" + - [ ] **B2.8** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project delete` command: + - [ ] **B2.8a** [Hamza] Define command signature: + ```python + @project.command("delete") + @click.argument("name") + @click.option("--force", "-f", is_flag=True, help="Force delete even if plans exist") + @click.option("--yes", is_flag=True, help="Skip confirmation") + ``` + - [ ] Commit: "feat(cli): add project delete command signature" + - [ ] **B2.8b** [Hamza] Implement deletion checks: + - [ ] Fetch project + - [ ] Check for active plans using this project: `plan_repo.count(project_id=project.project_id)` + - [ ] If plans exist and not --force: + - [ ] Display error: "Cannot delete project with {n} active plans. Use --force to delete anyway." + - [ ] List plan IDs (first 5) + - [ ] Exit with code 1 + - [ ] If --force, display warning: "Deleting project with {n} active plans" + - [ ] Commit: "feat(cli): implement project delete safety checks" + - [ ] **B2.8c** [Hamza] Implement deletion: + - [ ] Prompt for confirmation: "Delete project '{name}'? This cannot be undone. [y/N]" + - [ ] Support `--yes` to bypass confirmation + - [ ] If confirmed (or --yes), call `project_service.delete_project(project_id)` + - [ ] Display success: "Deleted project '{name}'" + - [ ] Commit: "feat(cli): implement project delete confirmation and execution" + - [ ] **B2.8d** [Hamza] Add ProjectService.delete_project() implementation: + - [ ] Delete all resources for project via resource_repo + - [ ] Delete project via project_repo + - [ ] Return True on success + - [ ] Commit: "feat(service): implement ProjectService.delete_project()" + - [ ] **B2.9** [Hamza] Register project commands in `src/cleveragents/cli/main.py`: + - [ ] **B2.9a** [Hamza] Import and register: + - [ ] Add `from cleveragents.cli.commands.project import project as project_group` + - [ ] Add `app.add_command(project_group)` in main app setup + - [ ] Verify `agents [--data-dir PATH] [--config-path PATH] project --help` shows all subcommands + - [ ] Commit: "feat(cli): register project commands in main CLI" + - [ ] **B2.9b** [Hamza] Add DI wiring for ProjectService: + - [ ] Update container.py to provide ProjectService + - [ ] Inject into CLI commands via Click context or similar pattern + - [ ] Commit: "feat(di): wire ProjectService into CLI" + - [ ] Tests: Behave + Robot for all project CLI commands + - [ ] **B2.10** [Rui] Write Behave scenarios in `features/project_cli.feature`: + - [ ] **B2.10a** [Rui] Project creation scenarios: + - [ ] Scenario: Create project with valid name succeeds + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project create --name local/test-project` + - [ ] Then the output contains "Created project: local/test-project" + - [ ] And the output contains "Project ID:" + - [ ] Scenario: Create project with description and tags + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project create --name local/test --description "My project" --tag python --tag backend` + - [ ] Then the project has description "My project" + - [ ] And the project has tags "python", "backend" + - [ ] Scenario: Create project with duplicate name fails + - [ ] Given a project "local/existing" exists + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project create --name local/existing` + - [ ] Then the exit code is 1 + - [ ] And the output contains "already exists" + - [ ] Scenario: Create project with invalid namespace fails + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project create --name 123invalid/test` + - [ ] Then the exit code is 1 + - [ ] And the output contains "Invalid namespace" + - [ ] Commit: "test(behave): add project create CLI scenarios" + - [ ] **B2.10b** [Rui] Add resource scenarios: + - [ ] Scenario: Add git repository resource to project + - [ ] Given a project "local/test" exists + - [ ] And a git repository exists at "/tmp/test-repo" + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project add-resource --project local/test --name source --type git_repository --location /tmp/test-repo --sandbox-strategy git_worktree` + - [ ] Then the output contains "Added resource 'source'" + - [ ] Scenario: Add filesystem resource to project + - [ ] Given a project "local/test" exists + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project add-resource --project local/test --name config --type filesystem --location /tmp/config --sandbox-strategy copy_on_write` + - [ ] Then the output contains "Added resource 'config'" + - [ ] Scenario: Add resource with incompatible sandbox strategy fails + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project add-resource --project local/test --name api --type api_endpoint --location https://api.example.com --sandbox-strategy git_worktree` + - [ ] Then the exit code is 1 + - [ ] And the output contains "incompatible" + - [ ] Scenario: Add resource with metadata + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project add-resource ... --metadata branch=main --metadata remote=origin` + - [ ] Then the resource has metadata key "branch" with value "main" + - [ ] Commit: "test(behave): add resource CLI scenarios" + - [ ] **B2.10c** [Rui] Remove resource scenarios: + - [ ] Scenario: Remove resource from project succeeds + - [ ] Given project "local/test" has resource "source" + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project remove-resource --project local/test --name source --yes` + - [ ] Then the output contains "Removed resource 'source'" + - [ ] Scenario: Remove non-existent resource fails gracefully + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project remove-resource --project local/test --name nonexistent --yes` + - [ ] Then the exit code is 1 + - [ ] And the output contains "not found" + - [ ] Commit: "test(behave): add remove-resource CLI scenarios" + - [ ] **B2.10d** [Rui] List and show scenarios: + - [ ] Scenario: List projects shows all projects + - [ ] Given projects "local/proj1" and "local/proj2" exist + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project list` + - [ ] Then the output contains "proj1" + - [ ] And the output contains "proj2" + - [ ] Scenario: List projects with namespace filter works + - [ ] Given projects "local/proj1" and "team/proj2" exist + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project list --namespace local` + - [ ] Then the output contains "proj1" + - [ ] And the output does not contain "proj2" + - [ ] Scenario: List projects JSON format works + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project list --format json` + - [ ] Then the output is valid JSON + - [ ] Scenario: Show project displays full details + - [ ] Given project "local/test" with 2 resources exists + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project show local/test` + - [ ] Then the output contains "local/test" + - [ ] And the output contains "Resources (2)" + - [ ] Commit: "test(behave): add list and show CLI scenarios" + - [ ] **B2.10e** [Rui] Validation config scenarios: + - [ ] Scenario: Set validation commands persists correctly + - [ ] Given project "local/test" exists + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project set-validation --project local/test --test-command "pytest" --lint-command "ruff check"` + - [ ] Then project "local/test" has test_command "pytest" + - [ ] And project "local/test" has lint_command "ruff check" + - [ ] Scenario: Clear validation config works + - [ ] Given project "local/test" has validation config + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project set-validation --project local/test --clear` + - [ ] Then project "local/test" has no validation config + - [ ] Commit: "test(behave): add validation config CLI scenarios" + - [ ] **B2.10f** [Rui] Delete scenarios: + - [ ] Scenario: Delete project succeeds + - [ ] Given project "local/test" exists with no plans + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project delete local/test --yes` + - [ ] Then the output contains "Deleted project" + - [ ] And project "local/test" no longer exists + - [ ] Scenario: Delete project with active plans blocked without --force + - [ ] Given project "local/test" exists + - [ ] And a plan uses project "local/test" + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project delete local/test --yes` + - [ ] Then the exit code is 1 + - [ ] And the output contains "active plans" + - [ ] Scenario: Delete project with active plans succeeds with --force + - [ ] Given project "local/test" exists with active plans + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project delete local/test --force --yes` + - [ ] Then the output contains "Deleted project" + - [ ] Commit: "test(behave): add delete CLI scenarios" + - [ ] **B2.11** [Rui] Write Robot integration test `robot/project_cli_integration.robot`: + - [ ] **B2.11a** [Rui] Full lifecycle test: + - [ ] Test: Full project lifecycle + - [ ] Create project with description and tags + - [ ] Add git repository resource + - [ ] Add filesystem resource + - [ ] Show project and verify all details + - [ ] Set validation commands + - [ ] List projects and verify presence + - [ ] Remove one resource + - [ ] Delete project + - [ ] Verify project no longer exists + - [ ] Commit: "test(robot): add full project lifecycle e2e test" + - [ ] **B2.11b** [Rui] Multi-resource test: + - [ ] Test: Project with multiple resources of different types + - [ ] Create project + - [ ] Add git repo resource (primary code) + - [ ] Add filesystem resource (docs) + - [ ] Add database resource (read-only) + - [ ] Verify is_remote is computed correctly (should be False - has local resources) + - [ ] Show project and verify all resources listed + - [ ] Commit: "test(robot): add multi-resource project e2e test" -**M2 MERGE GATE**: -- Register a git-checkout resource and link it to a project via CLI. -- Create a sandbox for the linked resource and verify isolation via tests. -- Project context commands and validation attachment visibility work and persist. -- `nox` passes with coverage >=97%. +- [ ] **Stage B3: Sandbox Framework** (Day 3-5) **[Luis + Hamza - Architectural, CRITICAL PATH]** + + **PARALLEL SUBTRACKS**: + - TRACK B3.protocol [Luis - Day 3 AM]: Protocol + Status + Factory (B3.1, B3.2, B3.5, B3.6) + - TRACK B3.git [Hamza - Day 3 PM - Day 4]: Git Worktree Implementation (B3.3) + - TRACK B3.fs [Hamza - Day 4]: Filesystem Implementation (B3.4) + - TRACK B3.manager [Luis - Day 4-5]: Manager + Merge (B3.7, B3.8) + - TRACK B3.tests [Rui - Day 3-5]: Tests in parallel with implementation + + - [ ] Code: Implement sandbox abstraction + - [ ] **B3.1** [Luis] Define `Sandbox` protocol in `src/cleveragents/infrastructure/sandbox/protocol.py`: + - [ ] **B3.1a** [Luis] Create file with necessary imports: + - [ ] Import `Protocol, runtime_checkable` from typing + - [ ] Import `ABC, abstractmethod` from abc + - [ ] Import `dataclasses` for result types + - [ ] Add module docstring explaining sandbox abstraction purpose + - [ ] Commit: "feat(sandbox): create protocol.py with imports" + - [ ] **B3.1b** [Luis] Define `SandboxContext` dataclass: + - [ ] `sandbox_id: str` - Unique identifier (ULID) for this sandbox instance + - [ ] `sandbox_path: str` - Root path where sandboxed files live + - [ ] `original_path: str` - Original resource location + - [ ] `resource_id: str` - ID of resource being sandboxed + - [ ] `plan_id: str` - ID of plan that created this sandbox + - [ ] `created_at: datetime` - When sandbox was created + - [ ] `metadata: dict[str, Any]` - Implementation-specific data (e.g., git branch name) + - [ ] Commit: "feat(sandbox): define SandboxContext dataclass" + - [ ] **B3.1c** [Luis] Define `CommitResult` dataclass: + - [ ] `sandbox_id: str` - Which sandbox was committed + - [ ] `success: bool` - Whether commit succeeded + - [ ] `commit_ref: str | None` - Git commit hash or equivalent reference + - [ ] `changed_files: list[str]` - List of files that were changed + - [ ] `added_files: list[str]` - List of files that were created + - [ ] `deleted_files: list[str]` - List of files that were removed + - [ ] `error: str | None` - Error message if success=False + - [ ] `timestamp: datetime` - When commit occurred + - [ ] Commit: "feat(sandbox): define CommitResult dataclass" + - [ ] **B3.1d** [Luis] Define `Sandbox` protocol: + ```python + @runtime_checkable + class Sandbox(Protocol): + """Protocol for resource sandboxing implementations.""" + + @property + def sandbox_id(self) -> str: + """Unique identifier for this sandbox.""" + ... + + @property + def resource(self) -> Resource: + """The resource being sandboxed.""" + ... + + @property + def status(self) -> SandboxStatus: + """Current status of the sandbox.""" + ... + + @property + def context(self) -> SandboxContext | None: + """Context after sandbox is created, None before.""" + ... + + def create(self, plan_id: str) -> SandboxContext: + """Initialize sandbox environment. Returns context with paths.""" + ... + + def get_path(self, resource_path: str) -> str: + """Translate resource-relative path to sandbox absolute path.""" + ... + + def commit(self, message: str | None = None) -> CommitResult: + """Finalize sandbox changes. Returns result with changed files.""" + ... + + def rollback(self) -> None: + """Discard all sandbox changes. Sandbox can still be used.""" + ... + + def cleanup(self) -> None: + """Remove sandbox artifacts. Sandbox cannot be used after this.""" + ... + ``` + - [ ] Commit: "feat(sandbox): define Sandbox protocol" + - [ ] **B3.2** [Luis] Define `SandboxStatus` enum in same file: + - [ ] **B3.2a** [Luis] Define status values with docstrings: + - [ ] `PENDING = "pending"` - Sandbox created but not yet initialized + - [ ] `CREATED = "created"` - Sandbox initialized, ready for use + - [ ] `ACTIVE = "active"` - Sandbox has been written to + - [ ] `COMMITTED = "committed"` - Changes have been applied to original + - [ ] `ROLLED_BACK = "rolled_back"` - Changes have been discarded + - [ ] `CLEANED_UP = "cleaned_up"` - Sandbox artifacts removed, terminal state + - [ ] `ERRORED = "errored"` - Sandbox operation failed + - [ ] Commit: "feat(sandbox): define SandboxStatus enum" + - [ ] **B3.2b** [Luis] Add status transition validation: + - [ ] `@classmethod def valid_transitions(cls) -> dict[SandboxStatus, list[SandboxStatus]]:` - Define allowed transitions + - [ ] PENDING → CREATED, ERRORED + - [ ] CREATED → ACTIVE, COMMITTED (no changes), CLEANED_UP + - [ ] ACTIVE → COMMITTED, ROLLED_BACK, ERRORED + - [ ] COMMITTED → CLEANED_UP + - [ ] ROLLED_BACK → ACTIVE (retry), CLEANED_UP + - [ ] ERRORED → CLEANED_UP + - [ ] CLEANED_UP → (terminal, no transitions) + - [ ] Commit: "feat(sandbox): add SandboxStatus transition validation" + - [ ] **B3.3** [Hamza] Implement `GitWorktreeSandbox` in `src/cleveragents/infrastructure/sandbox/git_worktree.py`: + - [ ] **B3.3a** [Hamza] Create class scaffold with constructor: + - [ ] Import subprocess for git commands + - [ ] Import logging, tempfile, shutil, os + - [ ] Import protocol types from protocol.py + - [ ] Define `class GitWorktreeSandbox:` implementing Sandbox protocol + - [ ] Constructor: `__init__(self, resource: Resource)`: + - [ ] Validate `resource.type == ResourceType.GIT_REPOSITORY` + - [ ] Validate `resource.sandbox_strategy == SandboxStrategy.GIT_WORKTREE` + - [ ] Store resource reference + - [ ] Initialize `_sandbox_id = ulid.new().str` + - [ ] Initialize `_status = SandboxStatus.PENDING` + - [ ] Initialize `_context: SandboxContext | None = None` + - [ ] Initialize `_worktree_path: str | None = None` + - [ ] Initialize `_branch_name: str | None = None` + - [ ] Commit: "feat(sandbox): add GitWorktreeSandbox class scaffold" + - [ ] **B3.3b** [Hamza] Implement `create(plan_id: str) -> SandboxContext`: + - [ ] Validate status is PENDING + - [ ] Generate unique branch name: `f"cleveragents-sandbox-{self._sandbox_id}"` + - [ ] Generate worktree path: `tempfile.mkdtemp(prefix=f"ca_git_worktree_{plan_id}_")` + - [ ] Determine repo root from resource.location (handle both path and URL) + - [ ] If resource is remote URL, first clone to temp location + - [ ] Run git command: `git worktree add {worktree_path} -b {branch_name}` + - [ ] Handle errors: if worktree fails, cleanup and raise + - [ ] Store paths in instance variables + - [ ] Create and store SandboxContext + - [ ] Update status to CREATED + - [ ] Log: "Created git worktree sandbox {sandbox_id} at {worktree_path}" + - [ ] Return context + - [ ] Commit: "feat(sandbox): implement GitWorktreeSandbox.create()" + - [ ] **B3.3c** [Hamza] Implement helper method for git commands: + - [ ] `def _run_git(self, args: list[str], cwd: str | None = None) -> subprocess.CompletedProcess`: + - [ ] Build full command: `["git"] + args` + - [ ] Run with `subprocess.run(capture_output=True, text=True, check=False)` + - [ ] Log command and output at DEBUG level + - [ ] If returncode != 0, log error at WARNING level + - [ ] Return CompletedProcess for caller to handle + - [ ] Commit: "feat(sandbox): add GitWorktreeSandbox._run_git() helper" + - [ ] **B3.3d** [Hamza] Implement `get_path(resource_path: str) -> str`: + - [ ] Validate sandbox is CREATED or ACTIVE + - [ ] Validate resource_path does not escape sandbox (no `..` traversal) + - [ ] Return `os.path.join(self._worktree_path, resource_path)` + - [ ] Commit: "feat(sandbox): implement GitWorktreeSandbox.get_path()" + - [ ] **B3.3e** [Hamza] Implement `commit(message: str | None = None) -> CommitResult`: + - [ ] Validate status is CREATED or ACTIVE + - [ ] Default message: `f"CleverAgents sandbox commit [{self._sandbox_id}]"` + - [ ] Run `git status --porcelain` to check for changes + - [ ] If no changes, return CommitResult(success=True, changed_files=[]) + - [ ] Run `git add -A` to stage all changes + - [ ] Parse `git diff --cached --name-status` to get changed/added/deleted lists + - [ ] Run `git commit -m "{message}"` to commit + - [ ] Get commit hash with `git rev-parse HEAD` + - [ ] Update status to COMMITTED + - [ ] Return CommitResult with all fields populated + - [ ] Commit: "feat(sandbox): implement GitWorktreeSandbox.commit()" + - [ ] **B3.3f** [Hamza] Implement `rollback() -> None`: + - [ ] Validate status is CREATED or ACTIVE + - [ ] Run `git checkout .` to discard modified files + - [ ] Run `git clean -fd` to remove untracked files + - [ ] Run `git reset HEAD` to unstage any staged changes + - [ ] Update status to ROLLED_BACK + - [ ] Log: "Rolled back git worktree sandbox {sandbox_id}" + - [ ] Commit: "feat(sandbox): implement GitWorktreeSandbox.rollback()" + - [ ] **B3.3g** [Hamza] Implement `cleanup() -> None`: + - [ ] Can be called from any non-terminal status + - [ ] Run `git worktree remove {worktree_path} --force` from repo root + - [ ] If worktree remove fails, try `shutil.rmtree(self._worktree_path)` as fallback + - [ ] Optionally delete the sandbox branch: `git branch -D {branch_name}` + - [ ] Update status to CLEANED_UP + - [ ] Clear context and paths + - [ ] Log: "Cleaned up git worktree sandbox {sandbox_id}" + - [ ] Commit: "feat(sandbox): implement GitWorktreeSandbox.cleanup()" + - [ ] **B3.3h** [Hamza] Add proper error handling: + - [ ] Define `SandboxError` base exception in protocol.py + - [ ] Define `SandboxCreationError(SandboxError)` - failed to create + - [ ] Define `SandboxCommitError(SandboxError)` - failed to commit + - [ ] Define `SandboxRollbackError(SandboxError)` - failed to rollback + - [ ] All methods should catch subprocess errors and wrap in SandboxError + - [ ] On error, update status to ERRORED and include original exception + - [ ] Commit: "feat(sandbox): add GitWorktreeSandbox error handling" + - [ ] **B3.4** [Hamza] Implement `FilesystemSandbox` in `src/cleveragents/infrastructure/sandbox/filesystem.py`: + - [ ] **B3.4a** [Hamza] Create class scaffold: + - [ ] Similar structure to GitWorktreeSandbox + - [ ] Constructor validates `resource.type == ResourceType.FILESYSTEM` + - [ ] Constructor validates `resource.sandbox_strategy == SandboxStrategy.COPY_ON_WRITE` + - [ ] Commit: "feat(sandbox): add FilesystemSandbox class scaffold" + - [ ] **B3.4b** [Hamza] Implement `create(plan_id: str) -> SandboxContext`: + - [ ] Generate sandbox path: `tempfile.mkdtemp(prefix=f"ca_fs_sandbox_{plan_id}_")` + - [ ] Copy resource directory to sandbox: `shutil.copytree(resource.location, sandbox_path, dirs_exist_ok=True)` + - [ ] Use `shutil.ignore_patterns()` to skip .git, node_modules, __pycache__ + - [ ] Record file hashes of original for diff detection later + - [ ] Create and store SandboxContext + - [ ] Update status to CREATED + - [ ] Commit: "feat(sandbox): implement FilesystemSandbox.create()" + - [ ] **B3.4c** [Hamza] Implement `get_path(resource_path: str) -> str`: + - [ ] Same pattern as GitWorktreeSandbox + - [ ] Commit: "feat(sandbox): implement FilesystemSandbox.get_path()" + - [ ] **B3.4d** [Hamza] Implement `commit(message: str | None = None) -> CommitResult`: + - [ ] Compare sandbox files with original (using recorded hashes) + - [ ] Build lists of changed/added/deleted files + - [ ] For each changed file: `shutil.copy2(sandbox_file, original_file)` + - [ ] For each new file: copy and create parent dirs as needed + - [ ] For each deleted file: `os.remove(original_file)` + - [ ] Use atomic operations where possible (write to .tmp then rename) + - [ ] Update status to COMMITTED + - [ ] Return CommitResult with file lists + - [ ] Commit: "feat(sandbox): implement FilesystemSandbox.commit()" + - [ ] **B3.4e** [Hamza] Implement `rollback() -> None`: + - [ ] For filesystem, rollback is implicit - just don't commit + - [ ] Re-copy original to sandbox to reset: `shutil.rmtree(sandbox); shutil.copytree(original, sandbox)` + - [ ] Update status to ROLLED_BACK + - [ ] Commit: "feat(sandbox): implement FilesystemSandbox.rollback()" + - [ ] **B3.4f** [Hamza] Implement `cleanup() -> None`: + - [ ] Remove sandbox directory: `shutil.rmtree(self._sandbox_path, ignore_errors=True)` + - [ ] Update status to CLEANED_UP + - [ ] Commit: "feat(sandbox): implement FilesystemSandbox.cleanup()" + - [ ] **B3.5** [Luis] Implement `NoSandbox` in `src/cleveragents/infrastructure/sandbox/no_sandbox.py`: + - [ ] **B3.5a** [Luis] Create class for non-sandboxable resources: + - [ ] For APIs, some cloud resources, etc. + - [ ] Constructor: accept any Resource with sandbox_strategy=NONE + - [ ] Commit: "feat(sandbox): add NoSandbox class scaffold" + - [ ] **B3.5b** [Luis] Implement all methods as passthrough or warnings: + - [ ] `create()`: Log WARNING "Resource {name} is not sandboxed - changes are immediate" + - [ ] `get_path(resource_path)`: Return original resource path unchanged + - [ ] `commit()`: Return CommitResult(success=True) - changes already applied + - [ ] `rollback()`: Log ERROR "Rollback not possible for non-sandboxed resource" + - [ ] `cleanup()`: No-op, status to CLEANED_UP + - [ ] Commit: "feat(sandbox): implement NoSandbox methods" + - [ ] **B3.6** [Luis] Implement `SandboxFactory` in `src/cleveragents/infrastructure/sandbox/factory.py`: + - [ ] **B3.6a** [Luis] Create factory class: + - [ ] Import all sandbox implementations + - [ ] Define `class SandboxFactory:` + - [ ] Commit: "feat(sandbox): add SandboxFactory scaffold" + - [ ] **B3.6b** [Luis] Implement `create_sandbox(resource: Resource) -> Sandbox`: + - [ ] Match on `resource.sandbox_strategy`: + ```python + match resource.sandbox_strategy: + case SandboxStrategy.GIT_WORKTREE: + return GitWorktreeSandbox(resource) + case SandboxStrategy.COPY_ON_WRITE: + return FilesystemSandbox(resource) + case SandboxStrategy.OVERLAY: + # Overlay not implemented yet, fall back to copy + logger.warning("Overlay not implemented, using copy-on-write") + return FilesystemSandbox(resource) + case SandboxStrategy.TRANSACTION_ROLLBACK: + raise NotImplementedError("Database sandboxing not yet implemented") + case SandboxStrategy.NONE: + return NoSandbox(resource) + case _: + raise ValueError(f"Unknown sandbox strategy: {resource.sandbox_strategy}") + ``` + - [ ] Commit: "feat(sandbox): implement SandboxFactory.create_sandbox()" + - [ ] **B3.6c** [Luis] Add validation helper: + - [ ] `@staticmethod def is_supported(resource: Resource) -> bool:` - Check if sandboxing is supported + - [ ] `@staticmethod def get_supported_strategies(resource_type: ResourceType) -> list[SandboxStrategy]:` - Valid combos + - [ ] Commit: "feat(sandbox): add SandboxFactory validation helpers" + - [ ] **B3.7** [Luis] Implement sandbox lifecycle management: + - [ ] **B3.7a** [Luis] Create `SandboxManager` in `src/cleveragents/infrastructure/sandbox/manager.py`: + - [ ] Import threading for lock management + - [ ] Import factory and protocol types + - [ ] Define `class SandboxManager:` + - [ ] Instance variables: + - [ ] `_factory: SandboxFactory` - injected via constructor + - [ ] `_active_sandboxes: dict[str, dict[str, Sandbox]]` - plan_id -> resource_id -> Sandbox + - [ ] `_lock: threading.RLock` - thread safety for sandbox tracking + - [ ] `_cleanup_on_exit: bool` - whether to cleanup on process exit (default True) + - [ ] Commit: "feat(sandbox): add SandboxManager scaffold" + - [ ] **B3.7b** [Luis] Implement `get_or_create_sandbox(plan_id: str, resource: Resource) -> Sandbox`: + - [ ] Acquire lock + - [ ] Check if sandbox already exists for this plan+resource + - [ ] If exists and status is usable (CREATED, ACTIVE, ROLLED_BACK), return it + - [ ] If exists but cleaned up, remove from tracking + - [ ] Create new sandbox via factory + - [ ] Call sandbox.create(plan_id) to initialize + - [ ] Store in _active_sandboxes + - [ ] Release lock + - [ ] Return sandbox + - [ ] This is the LAZY sandboxing pattern - only create when needed + - [ ] Commit: "feat(sandbox): implement SandboxManager.get_or_create_sandbox()" + - [ ] **B3.7c** [Luis] Implement `commit_all(plan_id: str) -> list[CommitResult]`: + - [ ] Get all sandboxes for plan_id + - [ ] For each sandbox with status ACTIVE: + - [ ] Call sandbox.commit() + - [ ] Collect CommitResult + - [ ] Return list of all results + - [ ] If any commit fails, don't rollback others (partial commit possible, caller decides) + - [ ] Commit: "feat(sandbox): implement SandboxManager.commit_all()" + - [ ] **B3.7d** [Luis] Implement `rollback_all(plan_id: str) -> None`: + - [ ] Get all sandboxes for plan_id + - [ ] For each sandbox with status ACTIVE: + - [ ] Call sandbox.rollback() + - [ ] Log any rollback failures but continue with others + - [ ] Commit: "feat(sandbox): implement SandboxManager.rollback_all()" + - [ ] **B3.7e** [Luis] Implement `cleanup_all(plan_id: str) -> None`: + - [ ] Get all sandboxes for plan_id + - [ ] For each sandbox: + - [ ] Call sandbox.cleanup() + - [ ] Remove plan_id entry from _active_sandboxes + - [ ] Commit: "feat(sandbox): implement SandboxManager.cleanup_all()" + - [ ] **B3.7f** [Luis] Implement `cleanup_abandoned() -> int`: + - [ ] Find sandbox directories matching pattern that aren't tracked + - [ ] Check if creating process is still alive (via PID file or lock) + - [ ] If process dead, clean up the directory + - [ ] Return count of cleaned sandboxes + - [ ] This is called on application startup + - [ ] Commit: "feat(sandbox): implement SandboxManager.cleanup_abandoned()" + - [ ] **B3.7g** [Luis] Add atexit handler for graceful cleanup: + - [ ] Register `atexit.register(self._cleanup_on_exit_handler)` + - [ ] Handler calls cleanup_all for all tracked plans + - [ ] Commit: "feat(sandbox): add SandboxManager atexit cleanup" + - [ ] **B3.8** [Luis] Implement merge strategies in `src/cleveragents/infrastructure/sandbox/merge.py`: + - [ ] **B3.8a** [Luis] Define merge types and protocol: + - [ ] Define `MergeResult` dataclass: + - [ ] `success: bool` + - [ ] `content: str | bytes` - merged content + - [ ] `has_conflicts: bool` + - [ ] `conflict_markers: list[tuple[int, int]]` - line ranges with conflicts + - [ ] Define `MergeStrategy(Protocol)`: + - [ ] Method `merge(base: str, ours: str, theirs: str) -> MergeResult` + - [ ] Commit: "feat(sandbox): define merge types and protocol" + - [ ] **B3.8b** [Luis] Implement `GitMergeStrategy`: + - [ ] Use `git merge-file` for three-way merge + - [ ] Write base, ours, theirs to temp files + - [ ] Run `git merge-file -p ours base theirs` + - [ ] Parse output for conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) + - [ ] Return MergeResult with content and conflict info + - [ ] Commit: "feat(sandbox): implement GitMergeStrategy" + - [ ] **B3.8c** [Luis] Implement `SequentialMergeStrategy`: + - [ ] For non-mergeable resources, apply changes in order + - [ ] "Theirs" (second change) always wins + - [ ] Return MergeResult(success=True, content=theirs) + - [ ] Commit: "feat(sandbox): implement SequentialMergeStrategy" + - [ ] **B3.8d** [Luis] Implement `JsonMergeStrategy`: + - [ ] Parse both as JSON + - [ ] Deep merge objects (recursive dict merge) + - [ ] Arrays: concatenate or last-wins based on config + - [ ] Return serialized merged JSON + - [ ] Commit: "feat(sandbox): implement JsonMergeStrategy" + - [ ] Tests: Integration tests for each sandbox type + - [ ] **B3.9** [Rui] Write Behave scenarios in `features/sandbox_git_worktree.feature`: + - [ ] **B3.9a** [Rui] Creation scenarios: + - [ ] Scenario: Create git worktree sandbox from local git repo + - [ ] Given a git repository at "/tmp/test-repo" with files + - [ ] When I create a GitWorktreeSandbox for that resource + - [ ] And I call sandbox.create("plan-123") + - [ ] Then a worktree exists at the sandbox path + - [ ] And the sandbox status is CREATED + - [ ] And the original repo is unchanged + - [ ] Scenario: Create sandbox from remote git URL (if applicable) + - [ ] Scenario: Create fails for non-git resource type + - [ ] Commit: "test(behave): add git worktree creation scenarios" + - [ ] **B3.9b** [Rui] Modification isolation scenarios: + - [ ] Scenario: Modify file in sandbox does not affect original + - [ ] Given a created git worktree sandbox + - [ ] When I write "modified content" to sandbox path "test.py" + - [ ] Then the original repo's "test.py" is unchanged + - [ ] And the sandbox status is ACTIVE + - [ ] Scenario: Create new file in sandbox does not appear in original + - [ ] Scenario: Delete file in sandbox does not delete original + - [ ] Commit: "test(behave): add git worktree isolation scenarios" + - [ ] **B3.9c** [Rui] Commit scenarios: + - [ ] Scenario: Commit sandbox creates git commit + - [ ] Given a sandbox with modified files + - [ ] When I call sandbox.commit("Test commit") + - [ ] Then a git commit exists with message "Test commit" + - [ ] And CommitResult.changed_files contains "test.py" + - [ ] And sandbox status is COMMITTED + - [ ] Scenario: Commit with no changes succeeds with empty file list + - [ ] Scenario: Commit includes all staged and unstaged changes + - [ ] Commit: "test(behave): add git worktree commit scenarios" + - [ ] **B3.9d** [Rui] Rollback and cleanup scenarios: + - [ ] Scenario: Rollback sandbox discards all changes + - [ ] Given a sandbox with modified files + - [ ] When I call sandbox.rollback() + - [ ] Then the sandbox files match original + - [ ] And sandbox status is ROLLED_BACK + - [ ] Scenario: Cleanup removes worktree and branch + - [ ] Scenario: Multiple sandboxes from same repo are isolated + - [ ] Commit: "test(behave): add git worktree rollback/cleanup scenarios" + - [ ] **B3.10** [Rui] Write Behave scenarios in `features/sandbox_filesystem.feature`: + - [ ] Similar structure to B3.9 but for filesystem sandbox + - [ ] Scenario: Create filesystem sandbox copies directory + - [ ] Scenario: Large directory copy uses efficient patterns + - [ ] Scenario: Ignore patterns (node_modules, .git) are skipped during copy + - [ ] Scenario: Modify file in sandbox does not affect original + - [ ] Scenario: Commit sandbox applies changes to original atomically + - [ ] Scenario: Rollback sandbox resets to original state + - [ ] Scenario: Cleanup removes temp directory + - [ ] Commit: "test(behave): add filesystem sandbox scenarios" + - [ ] **B3.11** [Rui] Write Robot integration test `robot/sandbox_integration.robot`: + - [ ] **B3.11a** [Rui] Full lifecycle test: + - [ ] Create real git repo with multiple files + - [ ] Create sandbox, modify files, commit + - [ ] Verify changes appear in repo + - [ ] Create second sandbox, rollback, verify no changes + - [ ] Cleanup, verify temp directories removed + - [ ] Commit: "test(robot): add full sandbox lifecycle e2e test" + - [ ] **B3.11b** [Rui] Parallel sandbox test: + - [ ] Create two sandboxes on same repo + - [ ] Modify different files in each + - [ ] Commit both + - [ ] Verify no cross-contamination + - [ ] Commit: "test(robot): add parallel sandbox isolation e2e test" + - [ ] Tests: Parallel execution isolation tests + - [ ] **B3.12** [Rui] Write Behave scenarios in `features/sandbox_isolation.feature`: + - [ ] Scenario: Two plans with sandboxes on same resource are isolated + - [ ] Given Plan A creates sandbox for resource R + - [ ] And Plan B creates sandbox for same resource R + - [ ] When Plan A writes "A content" to file.txt + - [ ] And Plan B writes "B content" to file.txt + - [ ] Then Plan A's sandbox shows "A content" + - [ ] And Plan B's sandbox shows "B content" + - [ ] And original file is unchanged + - [ ] Scenario: Plan A cannot see Plan B's intermediate changes + - [ ] Scenario: Commits from different plans require merge + - [ ] Commit: "test(behave): add sandbox isolation scenarios" + - [ ] Tests: Merge conflict resolution tests + - [ ] **B3.13** [Rui] Write Behave scenarios in `features/sandbox_merge.feature`: + - [ ] Scenario: Git merge strategy handles non-conflicting changes + - [ ] Given base content "line1\nline2\nline3" + - [ ] And ours changes line1 to "modified1" + - [ ] And theirs changes line3 to "modified3" + - [ ] When I merge with GitMergeStrategy + - [ ] Then result is "modified1\nline2\nmodified3" + - [ ] And has_conflicts is False + - [ ] Scenario: Git merge strategy marks conflicts appropriately + - [ ] Given both ours and theirs change line2 + - [ ] When I merge + - [ ] Then has_conflicts is True + - [ ] And content contains conflict markers + - [ ] Scenario: Sequential merge uses theirs content + - [ ] Scenario: JSON merge combines object properties + - [ ] Commit: "test(behave): add merge strategy scenarios" + +- [ ] **Stage B4: Resource Integration** (Day 6-7) **[Hamza]** + + **SEQUENTIAL ORDER**: B4.1 (Types) → B4.2 (Service scaffold) → B4.3 (Access) → B4.4 (Lazy sandbox) → B4.5 (Commit/Rollback) → B4.6 (Cleanup hooks) → B4.7 (Lifecycle integration) + + - [ ] Code: Connect resources to plan execution + - [ ] **B4.1** [Hamza] Define resource access types in `src/cleveragents/domain/models/core/resource_access.py`: + - [ ] **B4.1a** [Hamza] Define `AccessMode` enum: + - [ ] `READ = "read"` - Read-only access, may use original or sandbox + - [ ] `WRITE = "write"` - Write access, requires sandbox + - [ ] `EXECUTE = "execute"` - Execute commands in context + - [ ] Commit: "feat(domain): define AccessMode enum" + - [ ] **B4.1b** [Hamza] Define `ResourceAccess` dataclass: + - [ ] `resource_id: str` - Which resource is being accessed + - [ ] `plan_id: str` - Which plan is accessing + - [ ] `mode: AccessMode` - How resource is being accessed + - [ ] `sandbox: Sandbox | None` - Sandbox if write mode + - [ ] `effective_path: str` - Resolved path (sandbox or original) + - [ ] `accessed_at: datetime` - When access was granted + - [ ] `is_sandboxed: bool` - Whether using sandbox or original + - [ ] Commit: "feat(domain): define ResourceAccess dataclass" + - [ ] **B4.1c** [Hamza] Define `ResourceAccessTracker` dataclass: + - [ ] `plan_id: str` - Plan being tracked + - [ ] `accesses: dict[str, ResourceAccess]` - resource_id -> access + - [ ] `read_resources: set[str]` - Resources accessed for read + - [ ] `write_resources: set[str]` - Resources accessed for write + - [ ] `first_write_at: datetime | None` - When first write occurred + - [ ] Commit: "feat(domain): define ResourceAccessTracker" + - [ ] **B4.2** [Hamza] Create `ResourceService` scaffold in `src/cleveragents/application/services/resource_service.py`: + - [ ] **B4.2a** [Hamza] Define class with dependencies: + ```python + class ResourceService: + def __init__( + self, + sandbox_manager: SandboxManager, + project_repo: ProjectRepository, + resource_repo: ResourceRepository, + config: ResourceServiceConfig + ): + self._sandbox_manager = sandbox_manager + self._project_repo = project_repo + self._resource_repo = resource_repo + self._config = config + self._trackers: dict[str, ResourceAccessTracker] = {} # plan_id -> tracker + self._lock = threading.RLock() + ``` + - [ ] Commit: "feat(service): add ResourceService scaffold with dependencies" + - [ ] **B4.2b** [Hamza] Define `ResourceServiceConfig` in `src/cleveragents/config/settings.py`: + - [ ] `force_sandbox_for_reads: bool = False` - Always sandbox, even for reads + - [ ] `preserve_sandbox_on_failure: bool = True` - Keep sandbox for debugging on error + - [ ] `auto_cleanup_abandoned: bool = True` - Cleanup orphaned sandboxes on startup + - [ ] `max_sandboxes_per_plan: int = 10` - Limit sandboxes per plan + - [ ] Commit: "feat(config): add ResourceServiceConfig" + - [ ] **B4.3** [Hamza] Implement `access_resource()` method: + - [ ] **B4.3a** [Hamza] Core method signature: + ```python + def access_resource( + self, + plan_id: str, + resource: Resource, + mode: AccessMode = AccessMode.READ + ) -> ResourceAccess: + ``` + - [ ] Commit: "feat(service): add access_resource() signature" + - [ ] **B4.3b** [Hamza] Implement tracker initialization: + - [ ] Acquire lock + - [ ] If no tracker for plan_id, create one + - [ ] Check if resource already accessed + - [ ] If already accessed with same or higher mode, return existing access + - [ ] Commit: "feat(service): implement access_resource() tracker init" + - [ ] **B4.3c** [Hamza] Implement read access logic: + - [ ] If mode is READ and not force_sandbox_for_reads: + - [ ] Return access with effective_path = resource.location + - [ ] Set is_sandboxed = False + - [ ] Record in tracker's read_resources + - [ ] Commit: "feat(service): implement read access without sandbox" + - [ ] **B4.3d** [Hamza] Implement write access logic: + - [ ] If mode is WRITE: + - [ ] Call `sandbox_manager.get_or_create_sandbox(plan_id, resource)` + - [ ] Get sandbox.context.sandbox_path + - [ ] Set effective_path = sandbox_path + - [ ] Set is_sandboxed = True + - [ ] Record in tracker's write_resources + - [ ] Set first_write_at if not set + - [ ] Commit: "feat(service): implement write access with sandbox" + - [ ] **B4.3e** [Hamza] Implement access upgrade: + - [ ] If resource was accessed as READ but now needs WRITE: + - [ ] Create sandbox if not exists + - [ ] Update tracker to reflect write mode + - [ ] Return new ResourceAccess with sandboxed path + - [ ] Commit: "feat(service): implement access mode upgrade" + - [ ] **B4.4** [Hamza] Implement lazy sandboxing pattern: + - [ ] **B4.4a** [Hamza] Sandbox created only when write occurs: + - [ ] `access_resource(plan_id, resource, READ)` - no sandbox + - [ ] First `access_resource(plan_id, resource, WRITE)` - creates sandbox + - [ ] Subsequent writes to same resource - reuses existing sandbox + - [ ] Log: "Created sandbox for resource {name} on first write" + - [ ] Commit: "feat(service): implement lazy sandbox creation" + - [ ] **B4.4b** [Hamza] Track sandbox lifecycle per plan: + - [ ] Method `get_plan_sandboxes(plan_id: str) -> list[Sandbox]`: + - [ ] Return all sandboxes associated with plan + - [ ] Method `has_pending_changes(plan_id: str) -> bool`: + - [ ] Check if any sandbox has uncommitted changes + - [ ] Commit: "feat(service): add sandbox tracking methods" + - [ ] **B4.5** [Hamza] Implement commit and rollback methods: + - [ ] **B4.5a** [Hamza] Implement `commit_plan_resources()`: + ```python + def commit_plan_resources(self, plan_id: str, message: str | None = None) -> list[CommitResult]: + """Commit all sandbox changes for a plan.""" + results = [] + sandboxes = self._sandbox_manager.get_sandboxes(plan_id) + for sandbox in sandboxes: + if sandbox.status == SandboxStatus.ACTIVE: + result = sandbox.commit(message or f"CleverAgents plan {plan_id}") + results.append(result) + self._log_commit_result(result) + return results + ``` + - [ ] Commit: "feat(service): implement commit_plan_resources()" + - [ ] **B4.5b** [Hamza] Implement `rollback_plan_resources()`: + ```python + def rollback_plan_resources(self, plan_id: str) -> None: + """Rollback all sandbox changes for a plan.""" + sandboxes = self._sandbox_manager.get_sandboxes(plan_id) + for sandbox in sandboxes: + if sandbox.status in (SandboxStatus.CREATED, SandboxStatus.ACTIVE): + sandbox.rollback() + logger.info(f"Rolled back sandbox {sandbox.sandbox_id}") + ``` + - [ ] Commit: "feat(service): implement rollback_plan_resources()" + - [ ] **B4.5c** [Hamza] Implement `cleanup_plan_resources()`: + ```python + def cleanup_plan_resources(self, plan_id: str) -> None: + """Clean up all sandbox artifacts for a plan.""" + self._sandbox_manager.cleanup_all(plan_id) + # Remove tracker + with self._lock: + if plan_id in self._trackers: + del self._trackers[plan_id] + logger.info(f"Cleaned up all resources for plan {plan_id}") + ``` + - [ ] Commit: "feat(service): implement cleanup_plan_resources()" + - [ ] **B4.6** [Hamza] Add sandbox cleanup hooks: + - [ ] **B4.6a** [Hamza] Implement plan completion hook: + - [ ] Create `PlanCompletionHandler` that listens for plan state changes + - [ ] On transition to APPLIED: commit then cleanup + - [ ] On transition to CANCELLED: rollback then cleanup + - [ ] Commit: "feat(service): add plan completion cleanup hook" + - [ ] **B4.6b** [Hamza] Implement failure handling hook: + - [ ] On plan transition to ERRORED: + - [ ] If preserve_sandbox_on_failure: keep sandbox for debugging + - [ ] Log: "Sandbox preserved for debugging: {sandbox_path}" + - [ ] Otherwise: rollback and cleanup + - [ ] Commit: "feat(service): add failure handling hook" + - [ ] **B4.6c** [Hamza] Implement application exit hook: + - [ ] Register `atexit.register(self._cleanup_all_on_exit)` + - [ ] Handler iterates all active trackers + - [ ] Cleanup all sandboxes (don't commit - exit is unexpected) + - [ ] Log warning if any uncommitted changes lost + - [ ] Commit: "feat(service): add atexit cleanup hook" + - [ ] **B4.6d** [Hamza] Implement startup cleanup: + - [ ] Method `cleanup_abandoned_sandboxes() -> int`: + - [ ] Called on application startup + - [ ] Call `sandbox_manager.cleanup_abandoned()` + - [ ] Return count of cleaned sandboxes + - [ ] Log: "Cleaned up {n} abandoned sandboxes from previous session" + - [ ] Commit: "feat(service): add startup cleanup for abandoned sandboxes" + - [ ] **B4.7** [Hamza] Integrate with plan execution lifecycle: + - [ ] **B4.7a** [Hamza] Update `PlanLifecycleService.apply_plan()`: + - [ ] Before apply: verify all sandboxes have changes + - [ ] Call `resource_service.commit_plan_resources(plan_id)` + - [ ] If any commit fails, rollback all and raise + - [ ] On success: cleanup resources + - [ ] Commit: "feat(service): integrate ResourceService with apply_plan()" + - [ ] **B4.7b** [Hamza] Update `PlanLifecycleService.cancel_plan()`: + - [ ] Call `resource_service.rollback_plan_resources(plan_id)` + - [ ] Call `resource_service.cleanup_plan_resources(plan_id)` + - [ ] Commit: "feat(service): integrate ResourceService with cancel_plan()" + - [ ] **B4.7c** [Hamza] Add DI wiring for ResourceService: + - [ ] Update container.py to provide ResourceService + - [ ] Inject into PlanLifecycleService + - [ ] Commit: "feat(di): wire ResourceService into container" + - [ ] Tests: End-to-end tests for plan execution with sandboxed resources + - [ ] **B4.8** [Rui] Write Behave scenarios in `features/resource_service.feature`: + - [ ] **B4.8a** [Rui] Access mode scenarios: + - [ ] Scenario: First write access creates sandbox + - [ ] Given a plan "plan-123" and resource "repo" with sandbox_strategy=git_worktree + - [ ] When I call `resource_service.access_resource("plan-123", repo, WRITE)` + - [ ] Then a sandbox is created for the resource + - [ ] And the returned ResourceAccess.is_sandboxed is True + - [ ] And the effective_path points to the sandbox location + - [ ] Scenario: Read access without write uses original + - [ ] When I call `resource_service.access_resource("plan-123", repo, READ)` + - [ ] Then no sandbox is created + - [ ] And ResourceAccess.is_sandboxed is False + - [ ] And effective_path points to original resource.location + - [ ] Commit: "test(behave): add resource access mode scenarios" + - [ ] **B4.8b** [Rui] Sandbox reuse scenarios: + - [ ] Scenario: Multiple writes use same sandbox + - [ ] Given I accessed resource for WRITE once + - [ ] When I access the same resource for WRITE again + - [ ] Then the same sandbox is returned + - [ ] And only one sandbox exists for this plan+resource + - [ ] Scenario: Access upgrade from READ to WRITE creates sandbox + - [ ] Given I accessed resource for READ (no sandbox) + - [ ] When I access the same resource for WRITE + - [ ] Then a sandbox is created + - [ ] And the effective_path changes to sandbox path + - [ ] Commit: "test(behave): add sandbox reuse scenarios" + - [ ] **B4.8c** [Rui] Commit and rollback scenarios: + - [ ] Scenario: Plan completion commits and cleans up sandbox + - [ ] Given a plan with sandbox containing changes + - [ ] When the plan transitions to APPLIED + - [ ] Then commit_plan_resources is called + - [ ] And all changes are committed to the original resource + - [ ] And the sandbox is cleaned up + - [ ] Scenario: Plan failure rolls back sandbox + - [ ] Given a plan with sandbox containing changes + - [ ] When the plan transitions to ERRORED + - [ ] Then rollback_plan_resources is called (if not preserve_sandbox_on_failure) + - [ ] And no changes are committed + - [ ] Scenario: Plan failure preserves sandbox for debugging + - [ ] Given preserve_sandbox_on_failure=True + - [ ] When the plan transitions to ERRORED + - [ ] Then the sandbox is NOT cleaned up + - [ ] And a log message indicates sandbox location for debugging + - [ ] Commit: "test(behave): add commit/rollback scenarios" + - [ ] **B4.8d** [Rui] Cleanup scenarios: + - [ ] Scenario: Application exit cleans up all sandboxes + - [ ] Given multiple plans with active sandboxes + - [ ] When the application exits + - [ ] Then all sandbox directories are removed + - [ ] Scenario: Startup cleans up abandoned sandboxes + - [ ] Given orphaned sandbox directories from previous crash + - [ ] When the application starts + - [ ] Then abandoned sandboxes are cleaned up + - [ ] And a log message indicates how many were cleaned + - [ ] Commit: "test(behave): add cleanup scenarios" + - [ ] **B4.9** [Rui] Write Robot integration test `robot/resource_service_integration.robot`: + - [ ] **B4.9a** [Rui] Full lifecycle test: + - [ ] Test: Full plan execution with sandboxed git resource + - [ ] Create a real git repository with files + - [ ] Create a project with git resource + - [ ] Create a plan targeting the project + - [ ] Access resource for write (sandbox created) + - [ ] Modify files via sandbox path + - [ ] Complete plan (commit and cleanup) + - [ ] Verify changes appear in original git repo + - [ ] Verify sandbox directory is removed + - [ ] Commit: "test(robot): add full resource service e2e test" + - [ ] **B4.9b** [Rui] Multi-resource test: + - [ ] Test: Plan with multiple resources + - [ ] Create project with git repo + filesystem resources + - [ ] Access both for write + - [ ] Modify files in both sandboxes + - [ ] Commit all + - [ ] Verify both original resources have changes + - [ ] Commit: "test(robot): add multi-resource plan e2e test" + +- [ ] **Stage B5: Project Persistence** (Day 7-8) **[Hamza]** + + **SEQUENTIAL ORDER**: B5.1 (Projects migration) → B5.2 (Resources migration) → B5.3 (Project model) → B5.4 (Resource model) → B5.5 (ProjectRepository) → B5.6 (ResourceRepository) → B5.7 (Tests) + + - [ ] Code: Project/Resource database schema + - [ ] **B5.1** [Hamza] Create Alembic migration for `projects` table: + - [ ] **B5.1a** [Hamza] Generate migration file: + - [ ] Run `alembic revision --autogenerate -m "create_projects_table"` + - [ ] Commit: "chore(db): generate projects table migration" + - [ ] **B5.1b** [Hamza] Define schema: + ```python + def upgrade(): + op.create_table( + 'projects', + sa.Column('project_id', sa.Text(), nullable=False), + sa.Column('name', sa.Text(), nullable=False), + sa.Column('namespace', sa.Text(), nullable=False), + sa.Column('short_name', sa.Text(), nullable=False), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('tags', sa.JSON(), nullable=False, server_default='[]'), + sa.Column('is_remote', sa.Boolean(), nullable=False, server_default='false'), + sa.Column('validation_config', sa.JSON(), nullable=True), + sa.Column('context_config', sa.JSON(), nullable=True), + sa.Column('created_at', sa.Text(), nullable=False), + sa.Column('updated_at', sa.Text(), nullable=False), + sa.PrimaryKeyConstraint('project_id') + ) + op.create_index('ix_projects_name', 'projects', ['name'], unique=True) + op.create_index('ix_projects_namespace', 'projects', ['namespace']) + op.create_index('ix_projects_namespace_short_name', 'projects', ['namespace', 'short_name'], unique=True) + ``` + - [ ] Commit: "feat(db): add projects table schema" + - [ ] **B5.1c** [Hamza] Add downgrade: + ```python + def downgrade(): + op.drop_index('ix_projects_namespace_short_name') + op.drop_index('ix_projects_namespace') + op.drop_index('ix_projects_name') + op.drop_table('projects') + ``` + - [ ] Commit: "feat(db): add projects table downgrade" + - [ ] **B5.2** [Hamza] Create Alembic migration for `resources` table: + - [ ] **B5.2a** [Hamza] Generate migration file: + - [ ] Run `alembic revision --autogenerate -m "create_resources_table"` + - [ ] Commit: "chore(db): generate resources table migration" + - [ ] **B5.2b** [Hamza] Define schema: + ```python + def upgrade(): + op.create_table( + 'resources', + sa.Column('resource_id', sa.Text(), nullable=False), + sa.Column('project_id', sa.Text(), nullable=False), + sa.Column('name', sa.Text(), nullable=False), + sa.Column('type', sa.Text(), nullable=False), + sa.Column('location', sa.Text(), nullable=False), + sa.Column('is_remote', sa.Boolean(), nullable=False, server_default='false'), + sa.Column('sandbox_strategy', sa.Text(), nullable=False), + sa.Column('read_only', sa.Boolean(), nullable=False, server_default='false'), + sa.Column('metadata', sa.JSON(), nullable=False, server_default='{}'), + sa.Column('created_at', sa.Text(), nullable=False), + sa.PrimaryKeyConstraint('resource_id'), + sa.ForeignKeyConstraint(['project_id'], ['projects.project_id'], ondelete='CASCADE') + ) + op.create_index('ix_resources_project_id', 'resources', ['project_id']) + op.create_unique_constraint('uq_resources_project_name', 'resources', ['project_id', 'name']) + ``` + - [ ] Commit: "feat(db): add resources table schema with FK to projects" + - [ ] **B5.2c** [Hamza] Add downgrade: + - [ ] Drop constraint, index, and table + - [ ] Commit: "feat(db): add resources table downgrade" + - [ ] **B5.3** [Hamza] Create `ProjectModel` in `src/cleveragents/infrastructure/database/models.py`: + - [ ] **B5.3a** [Hamza] Define SQLAlchemy model: + ```python + class ProjectModel(Base): + __tablename__ = 'projects' + + project_id = Column(Text, primary_key=True) + name = Column(Text, nullable=False, unique=True) + namespace = Column(Text, nullable=False, index=True) + short_name = Column(Text, nullable=False) + description = Column(Text, nullable=True) + tags = Column(JSON, nullable=False, default=list) + is_remote = Column(Boolean, nullable=False, default=False) + validation_config = Column(JSON, nullable=True) + context_config = Column(JSON, nullable=True) + created_at = Column(Text, nullable=False) + updated_at = Column(Text, nullable=False) + + # Relationship to resources + resources = relationship("ResourceModel", back_populates="project", cascade="all, delete-orphan") + ``` + - [ ] Commit: "feat(db): add ProjectModel SQLAlchemy class" + - [ ] **B5.3b** [Hamza] Add domain conversion methods: + ```python + def to_domain(self) -> Project: + return Project( + project_id=self.project_id, + name=self.name, + namespace=self.namespace, + description=self.description, + tags=self.tags or [], + resources=[r.to_domain() for r in self.resources], + validation_config=ValidationConfig(**self.validation_config) if self.validation_config else None, + context_config=ContextConfig(**self.context_config) if self.context_config else ContextConfig(), + created_at=datetime.fromisoformat(self.created_at), + updated_at=datetime.fromisoformat(self.updated_at), + ) + + @classmethod + def from_domain(cls, project: Project) -> "ProjectModel": + return cls( + project_id=project.project_id, + name=project.namespaced_name, + namespace=project.namespace, + short_name=project.name, + description=project.description, + tags=project.tags, + is_remote=project.is_remote, + validation_config=project.validation_config.model_dump() if project.validation_config else None, + context_config=project.context_config.model_dump(), + created_at=project.created_at.isoformat(), + updated_at=project.updated_at.isoformat(), + ) + ``` + - [ ] Commit: "feat(db): add ProjectModel domain conversion methods" + - [ ] **B5.4** [Hamza] Create `ResourceModel` in same file: + - [ ] **B5.4a** [Hamza] Define SQLAlchemy model: + ```python + class ResourceModel(Base): + __tablename__ = 'resources' + + resource_id = Column(Text, primary_key=True) + project_id = Column(Text, ForeignKey('projects.project_id', ondelete='CASCADE'), nullable=False) + name = Column(Text, nullable=False) + type = Column(Text, nullable=False) + location = Column(Text, nullable=False) + is_remote = Column(Boolean, nullable=False, default=False) + sandbox_strategy = Column(Text, nullable=False) + read_only = Column(Boolean, nullable=False, default=False) + metadata = Column(JSON, nullable=False, default=dict) + created_at = Column(Text, nullable=False) + + # Relationship back to project + project = relationship("ProjectModel", back_populates="resources") + + __table_args__ = ( + UniqueConstraint('project_id', 'name', name='uq_resources_project_name'), + ) + ``` + - [ ] Commit: "feat(db): add ResourceModel SQLAlchemy class" + - [ ] **B5.4b** [Hamza] Add domain conversion methods: + - [ ] `to_domain()` - Convert to Resource domain model + - [ ] `from_domain()` - Create from Resource domain model + - [ ] Handle enum conversions (ResourceType, SandboxStrategy) + - [ ] Commit: "feat(db): add ResourceModel domain conversion methods" + - [ ] **B5.5** [Hamza] Implement `ProjectRepository` in `src/cleveragents/infrastructure/database/repositories.py`: + - [ ] **B5.5a** [Hamza] Define class with session factory: + ```python + class ProjectRepository: + def __init__(self, session_factory: Callable[[], Session]): + self._session_factory = session_factory + ``` + - [ ] Commit: "feat(repo): add ProjectRepository scaffold" + - [ ] **B5.5b** [Hamza] Implement `create(project: Project) -> Project`: + - [ ] Create ProjectModel from domain + - [ ] Add to session + - [ ] Handle duplicate name: raise `DuplicateProjectError` + - [ ] Commit transaction + - [ ] Return created project + - [ ] Commit: "feat(repo): implement ProjectRepository.create()" + - [ ] **B5.5c** [Hamza] Implement `get_by_id(project_id: str) -> Project | None`: + - [ ] Query by primary key with eager load of resources + - [ ] Convert to domain or return None + - [ ] Commit: "feat(repo): implement ProjectRepository.get_by_id()" + - [ ] **B5.5d** [Hamza] Implement `get_by_name(name: str) -> Project | None`: + - [ ] Query by namespaced name (unique index) + - [ ] Eager load resources + - [ ] Convert to domain + - [ ] Commit: "feat(repo): implement ProjectRepository.get_by_name()" + - [ ] **B5.5e** [Hamza] Implement `get_with_resources(project_id: str) -> Project | None`: + - [ ] Same as get_by_id but ensures resources are loaded + - [ ] Use `options(joinedload(ProjectModel.resources))` + - [ ] Commit: "feat(repo): implement ProjectRepository.get_with_resources()" + - [ ] **B5.5f** [Hamza] Implement `list_all(namespace: str | None = None) -> list[Project]`: + - [ ] Query all projects + - [ ] Filter by namespace if provided + - [ ] Order by namespace ASC, short_name ASC + - [ ] Convert all to domain + - [ ] Commit: "feat(repo): implement ProjectRepository.list_all()" + - [ ] **B5.5g** [Hamza] Implement `update(project: Project) -> Project`: + - [ ] Fetch existing by project_id + - [ ] Update all fields from domain + - [ ] Update `updated_at` to now + - [ ] Commit and return + - [ ] Commit: "feat(repo): implement ProjectRepository.update()" + - [ ] **B5.5h** [Hamza] Implement `delete(project_id: str) -> bool`: + - [ ] Delete project (cascade deletes resources via FK) + - [ ] Return True if deleted + - [ ] Commit: "feat(repo): implement ProjectRepository.delete()" + - [ ] **B5.6** [Hamza] Implement `ResourceRepository` in same file: + - [ ] **B5.6a** [Hamza] Define class: + - [ ] Same pattern as ProjectRepository + - [ ] Commit: "feat(repo): add ResourceRepository scaffold" + - [ ] **B5.6b** [Hamza] Implement `create(resource: Resource, project_id: str) -> Resource`: + - [ ] Create ResourceModel with project_id link + - [ ] Handle duplicate name within project: raise `DuplicateResourceError` + - [ ] Commit: "feat(repo): implement ResourceRepository.create()" + - [ ] **B5.6c** [Hamza] Implement `get_by_project(project_id: str) -> list[Resource]`: + - [ ] Query all resources with given project_id + - [ ] Order by name ASC + - [ ] Commit: "feat(repo): implement ResourceRepository.get_by_project()" + - [ ] **B5.6d** [Hamza] Implement `get_by_name(project_id: str, name: str) -> Resource | None`: + - [ ] Query by unique (project_id, name) pair + - [ ] Commit: "feat(repo): implement ResourceRepository.get_by_name()" + - [ ] **B5.6e** [Hamza] Implement `delete(resource_id: str) -> bool`: + - [ ] Delete resource by ID + - [ ] Commit: "feat(repo): implement ResourceRepository.delete()" + - [ ] Tests: Integration tests for persistence + - [ ] **B5.7** [Rui] Write Behave scenarios in `features/project_persistence.feature`: + - [ ] **B5.7a** [Rui] Project persistence scenarios: + - [ ] Scenario: Create project persists to database + - [ ] Given no project "local/test" exists + - [ ] When I create project "local/test" via ProjectRepository + - [ ] Then querying by name returns the project + - [ ] And project_id is a valid ULID + - [ ] Scenario: Update project persists changes + - [ ] Given project "local/test" exists + - [ ] When I update description to "New description" + - [ ] Then re-querying shows updated description + - [ ] And updated_at has changed + - [ ] Commit: "test(behave): add project persistence scenarios" + - [ ] **B5.7b** [Rui] Resource persistence scenarios: + - [ ] Scenario: Add resource persists and links to project + - [ ] Given project "local/test" exists + - [ ] When I add resource "source" to project + - [ ] Then ResourceRepository.get_by_project() returns the resource + - [ ] And resource.project_id matches the project + - [ ] Scenario: Get project includes all resources + - [ ] Given project "local/test" with 3 resources + - [ ] When I call ProjectRepository.get_with_resources() + - [ ] Then project.resources has 3 items + - [ ] And each resource has correct fields + - [ ] Commit: "test(behave): add resource persistence scenarios" + - [ ] **B5.7c** [Rui] Cascade scenarios: + - [ ] Scenario: Delete project cascades to resources + - [ ] Given project "local/test" with 2 resources + - [ ] When I delete the project + - [ ] Then ResourceRepository.get_by_project() returns empty list + - [ ] And the resource records no longer exist in database + - [ ] Commit: "test(behave): add cascade delete scenarios" + - [ ] **B5.7d** [Rui] Uniqueness scenarios: + - [ ] Scenario: Duplicate project name raises error + - [ ] Given project "local/test" exists + - [ ] When I try to create another "local/test" + - [ ] Then DuplicateProjectError is raised + - [ ] Scenario: Duplicate resource name within project raises error + - [ ] Given project "local/test" has resource "source" + - [ ] When I try to add another resource named "source" + - [ ] Then DuplicateResourceError is raised + - [ ] Scenario: Same resource name in different projects is allowed + - [ ] Given project "local/proj1" has resource "source" + - [ ] When I add resource "source" to project "local/proj2" + - [ ] Then it succeeds without error + - [ ] Commit: "test(behave): add uniqueness constraint scenarios" + - [ ] Method `get_by_project(project_id: str) -> list[Resource]` + - [ ] **B5.5** [Hamza] Create database model classes: + - [ ] `ProjectModel(Base)` with `to_domain()` and `from_domain()` + - [ ] `ResourceModel(Base)` with `to_domain()` and `from_domain()` + - [ ] Tests: Integration tests for persistence + - [ ] **B5.6** [Rui] Write Behave scenarios in `features/project_persistence.feature`: + - [ ] Scenario: Create project persists to database + - [ ] Scenario: Add resource persists and links to project + - [ ] Scenario: Get project includes all resources + - [ ] Scenario: Delete project cascades to resources **M2 SUCCESS CRITERIA**: -- Resource registry supports resource types, resources, and DAG links with persistence (tables + repositories). -- Projects can link/unlink resources with CLI commands for resource types/resources/projects (list/show/tree included). -- Validation attachments (via `agents validation attach/detach`) appear in `project show` outputs. -- Git-checkout sandbox isolates changes; copy_on_write strategy returns clear NotImplementedError for fs-directory (documented). -- Resource/project services are DI-wired and exercised by Behave + Robot suites. -- `nox` passes with coverage >=97% across resource/project suites. +- [ ] Can create a project with resources via CLI +- [ ] Git repository resources can be sandboxed with worktrees +- [ ] Filesystem resources can be sandboxed with copy-on-write +- [ ] Plan execution uses sandboxed resources +- [ ] Sandbox cleanup works on success and failure + --- ### Section 5: Actors, Skills & Tool Execution [WORKSTREAM C - Aditya Lead] **Target: Milestone M3 (+14 days)** -**Week 2 focus**: Actor YAML, compilation, skills, and tool-based change tracking. +**WEEK 2 - CRITICAL FOR MVP** -**Parallel Group C0: Tool Registry + Validation System [Jeff + Luis]** (start Day 5; precedes C1/C3) - **PARALLEL SUBTRACK C0.domain [Jeff]**: Tool + Validation domain models + schemas - **PARALLEL SUBTRACK C0.registry [Luis]**: Tool registry persistence + repositories - **PARALLEL SUBTRACK C0.cli [Rui]**: CLI commands for tools/validations - **SEQUENTIAL MERGE NOTE**: C0.domain must land before C0.registry/cli; C0.registry before C3 context wiring. C0.registry migrations must rebase after A5.alpha; C0.binding should wait for B1.core resource type constraints to validate bindings. -- [ ] **COMMIT (Owner: Jeff | Group: C0.domain) - Commit message: "feat(tool): add tool and validation domain models"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add `Tool` model in `src/cleveragents/domain/models/core/tool.py` with namespaced name, description, source type, input/output JSON schema, and capability metadata (read/write/checkpointable). - - [ ] Code [Jeff]: Add `ResourceBinding` model with slot definitions and binding modes (context, static, parameter), plus required/optional flags. - - [ ] Code [Jeff]: Add `Validation` model as Tool subtype with `mode`, `wraps`, and `transform` fields; enforce read-only constraints. - - [ ] Code [Jeff]: Add enums for ToolSource, ToolType (tool/validation), and ValidationMode. - - [ ] Code [Jeff]: Add `docs/schema/tool.schema.yaml` and `docs/schema/validation.schema.yaml` with required fields, `wraps`/`transform` rules, and resource binding definitions. - - [ ] Code [Jeff]: Add YAML loader in `src/cleveragents/tool/schema.py` that validates schema version, normalizes keys, and returns Tool/Validation domain models. - - [ ] Code [Jeff]: Add example configs under `examples/tools/` and `examples/validations/` (plain tool, validation, wrapped validation) for tests. - - [ ] Docs [Jeff]: Add `docs/reference/tool_model.md` and `docs/reference/validation_model.md` with examples. - - [ ] Tests (Behave) [Jeff]: Add `features/tool_model.feature` for schema validation, resource binding rules, validation constraints, and YAML loader errors. - - [ ] Tests (Robot) [Jeff]: Add `robot/tool_model.robot` smoke tests for model creation. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/tool_model_bench.py` for schema validation throughput (model + YAML loader). - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(tool): add tool and validation domain models"`. -- [ ] **COMMIT (Owner: Luis | Group: C0.registry) - Commit message: "feat(tool): add tool registry persistence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add DB tables for `tools`, `tool_bindings`, and `validation_attachments` with indexes on namespaced name and type. - - [ ] Code [Luis]: Define `tools` columns: `namespaced_name`, `namespace`, `tool_type`, `source`, `description`, `input_schema_json`, `output_schema_json`, `capability_json`, `metadata_json`, `yaml_text`, timestamps. - - [ ] Code [Luis]: Define `tool_bindings` columns: `binding_id` ULID, `tool_name`, `slot_name`, `binding_mode`, `resource_type`, `required`, `static_resource_id`, `static_resource_name`, timestamps. - - [ ] Code [Luis]: Define `validation_attachments` columns: `attachment_id` ULID, `validation_name`, `resource_id`, optional `project_name`, optional `plan_id`, `args_json`, timestamps. - - [ ] Code [Luis]: Add uniqueness constraints for `tools.namespaced_name` and `tool_bindings(tool_name, slot_name)`; index `validation_attachments.resource_id`. - - [ ] Code [Luis]: Implement ToolRepository + ValidationAttachmentRepository with list/show filters and eager-loading of bindings. - - [ ] Code [Luis]: Add ToolRegistryService for register/update/remove/list/show with name conflict checks and tool/validation type enforcement. - - [ ] Docs [Luis]: Update `docs/reference/database_schema.md` with tool/validation tables. - - [ ] Tests (Behave) [Luis]: Add `features/tool_registry.feature` for register/update/remove and validation-only constraints. - - [ ] Tests (Robot) [Luis]: Add `robot/tool_registry.robot` for list/show smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/tool_registry_bench.py` for registry list performance. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(tool): add tool registry persistence"`. -- [ ] **COMMIT (Owner: Jeff | Group: C0.binding) - Commit message: "feat(tool): add resource binding resolution"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Implement binding resolution for contextual, static, and parameter bindings with type compatibility checks. - - [ ] Code [Jeff]: Add resolution helpers for resource name/ULID lookup and project-scoped filtering. - - [ ] Docs [Jeff]: Add `docs/reference/tool_bindings.md` with resolution order and examples. - - [ ] Tests (Behave) [Jeff]: Add binding resolution scenarios (context vs static vs parameter). - - [ ] Tests (Robot) [Jeff]: Add Robot test resolving a bound resource by name. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/binding_resolution_bench.py` for resolution latency. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(tool): add resource binding resolution"`. -- [ ] **COMMIT (Owner: Rui | Group: C0.cli) - Commit message: "feat(cli): add tool and validation commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Rui]: Implement `agents tool add/remove/list/show` with YAML config input and `--type` filter. - - [ ] Code [Rui]: Implement `agents validation add/attach/detach` commands and enforce validation-only name use. - - [ ] Code [Rui]: Support `validation attach --project/--plan` flags and store attachment args. - - [ ] Docs [Rui]: Update CLI reference with tool/validation commands and output format. - - [ ] Tests (Behave) [Rui]: Add CLI scenarios for tool/validation registration and attachment. - - [ ] Tests (Robot) [Rui]: Add Robot CLI suites for tool and validation commands. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_cli_bench.py` for CLI parsing overhead. - - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "feat(cli): add tool and validation commands"`. +- [ ] **Stage C1: Actor YAML Schema Formalization** (Day 5-6) **[Aditya - Domain Expert]** + + **SEQUENTIAL ORDER**: C1.1 (Enums) → C1.2 (Tool/Route models) → C1.3 (Context model) → C1.4 (ActorConfigSchema) → C1.5 (Examples) → C1.6 (Docs) → C1.7 (Tests) + + - [ ] Code: Formalize actor YAML schema + - [ ] **C1.1** [Aditya] Define core enums in `src/cleveragents/actor/schema.py`: + - [ ] **C1.1a** [Aditya] Create file with `ActorType` enum: + ```python + class ActorType(str, Enum): + """Type of actor determining execution behavior.""" + LLM = "llm" # Single LLM with system prompt + TOOL = "tool" # Collection of callable tools + GRAPH = "graph" # Multi-node StateGraph with routing + ``` + - [ ] Commit: "feat(actor): define ActorType enum" + - [ ] **C1.1b** [Aditya] Define `NodeType` enum: + ```python + class NodeType(str, Enum): + """Type of node in a graph actor.""" + AGENT = "agent" # LLM agent node + TOOL = "tool" # Tool execution node + CONDITIONAL = "conditional" # Routing/conditional node + SUBGRAPH = "subgraph" # Nested actor reference + ``` + - [ ] Commit: "feat(actor): define NodeType enum" + - [ ] **C1.1c** [Aditya] Define `ContextView` enum: + ```python + class ContextView(str, Enum): + """Role-based context filtering for actors.""" + STRATEGIST = "strategist" # High-level architecture, READMEs + EXECUTOR = "executor" # Precise code sections for edits + REVIEWER = "reviewer" # Diffs, tests, style guides + FULL = "full" # Complete context (default) + ``` + - [ ] Commit: "feat(actor): define ContextView enum" + - [ ] **C1.2** [Aditya] Define tool and route models: + - [ ] **C1.2a** [Aditya] Define `ToolParameter` model: + ```python + class ToolParameter(BaseModel): + """Parameter definition for inline tool.""" + name: str = Field(..., description="Parameter name") + type: str = Field(..., description="JSON Schema type (string, integer, object, etc.)") + description: str = Field(..., description="What this parameter is for") + required: bool = Field(default=True) + default: Any = Field(default=None) + enum: list[str] | None = Field(default=None, description="Allowed values") + ``` + - [ ] Commit: "feat(actor): define ToolParameter model" + - [ ] **C1.2b** [Aditya] Define `ToolDefinition` model: + ```python + class ToolDefinition(BaseModel): + """Inline tool/skill definition in actor YAML.""" + name: str = Field(..., min_length=1, max_length=100, description="Tool identifier") + description: str = Field(..., description="What the tool does (shown to LLM)") + parameters: list[ToolParameter] = Field(default_factory=list) + returns: str = Field(default="Any", description="Return type documentation") + code: str = Field(..., description="Python code to execute") + timeout_seconds: int = Field(default=30, ge=1, le=300) + + @field_validator('code') + @classmethod + def validate_code_syntax(cls, v: str) -> str: + """Validate Python syntax without executing.""" + try: + compile(v, '', 'exec') + except SyntaxError as e: + raise ValueError(f"Invalid Python syntax: {e}") + return v + ``` + - [ ] Commit: "feat(actor): define ToolDefinition model with code validation" + - [ ] **C1.2c** [Aditya] Define `EdgeDefinition` model: + ```python + class EdgeDefinition(BaseModel): + """Edge in actor graph topology.""" + source: str = Field(..., description="Source node name") + target: str = Field(..., description="Target node name") + condition: str | None = Field(default=None, description="Python expression for conditional routing") + label: str | None = Field(default=None, description="Edge label for visualization") + ``` + - [ ] Commit: "feat(actor): define EdgeDefinition model" + - [ ] **C1.2d** [Aditya] Define `NodeDefinition` model: + ```python + class NodeDefinition(BaseModel): + """Node in actor graph.""" + name: str = Field(..., description="Unique node identifier") + type: NodeType = Field(..., description="Type of node") + # For agent nodes: + model: str | None = Field(default=None, description="Model name for agent nodes") + system_prompt: str | None = Field(default=None) + tools: list[str] | None = Field(default=None, description="Tool names available to this agent") + # For tool nodes: + tool: str | None = Field(default=None, description="Tool name to execute") + # For subgraph nodes: + actor: str | None = Field(default=None, description="Actor reference (e.g., local/other-actor)") + ``` + - [ ] Commit: "feat(actor): define NodeDefinition model" + - [ ] **C1.2e** [Aditya] Define `RouteDefinition` model: + ```python + class RouteDefinition(BaseModel): + """Complete graph topology definition.""" + nodes: list[NodeDefinition] = Field(..., min_length=1) + edges: list[EdgeDefinition] = Field(default_factory=list) + entry_point: str = Field(..., description="Name of starting node") + + @model_validator(mode='after') + def validate_topology(self) -> Self: + """Validate graph is well-formed.""" + node_names = {n.name for n in self.nodes} + if self.entry_point not in node_names: + raise ValueError(f"entry_point '{self.entry_point}' not in nodes") + for edge in self.edges: + if edge.source not in node_names: + raise ValueError(f"Edge source '{edge.source}' not in nodes") + if edge.target not in node_names: + raise ValueError(f"Edge target '{edge.target}' not in nodes") + return self + ``` + - [ ] Commit: "feat(actor): define RouteDefinition with topology validation" + - [ ] **C1.3** [Aditya] Define context/memory configuration: + - [ ] **C1.3a** [Aditya] Define `MemoryConfig` model: + ```python + class MemoryConfig(BaseModel): + """Memory/conversation history settings.""" + enabled: bool = Field(default=True, description="Whether to maintain history") + max_turns: int = Field(default=20, ge=1, le=100, description="Max conversation turns") + summarization_threshold: int = Field(default=15, description="Turns before summarizing") + include_system_messages: bool = Field(default=True) + ``` + - [ ] Commit: "feat(actor): define MemoryConfig model" + - [ ] **C1.3b** [Aditya] Define `ContextConfigSchema` model: + ```python + class ContextConfigSchema(BaseModel): + """Context window configuration for actor.""" + context_window_fraction: float = Field(default=0.8, ge=0.1, le=1.0, + description="Fraction of model's context window to use") + context_view: ContextView = Field(default=ContextView.FULL, + description="Role-based context filtering") + include_file_patterns: list[str] = Field(default_factory=list, + description="Glob patterns for files to always include") + exclude_file_patterns: list[str] = Field(default_factory=list, + description="Glob patterns for files to never include") + max_file_size_kb: int = Field(default=100, description="Max file size to include") + ``` + - [ ] Commit: "feat(actor): define ContextConfigSchema model" + - [ ] **C1.4** [Aditya] Define main `ActorConfigSchema`: + - [ ] **C1.4a** [Aditya] Create comprehensive model: + ```python + class ActorConfigSchema(BaseModel): + """Complete actor configuration from YAML.""" + # Identity + version: str = Field(default="3", description="Config schema version") + name: str = Field(..., description="Actor name (without namespace)") + namespace: str = Field(default="local") + description: str | None = Field(default=None) + tags: list[str] = Field(default_factory=list) + + # Type and provider + type: ActorType = Field(..., description="Actor type") + model: str | None = Field(default=None, description="LLM model name") + provider: str | None = Field(default=None, description="Provider: openai, anthropic, etc.") + + # LLM configuration + system_prompt: str | None = Field(default=None) + temperature: float = Field(default=0.7, ge=0.0, le=2.0) + max_tokens: int | None = Field(default=None) + + # Tools/skills + tools: list[ToolDefinition] = Field(default_factory=list, + description="Inline tool definitions") + builtin_tools: list[str] = Field(default_factory=list, + description="Names of built-in tools to include") + mcp_servers: list[str] = Field(default_factory=list, + description="MCP server identifiers to connect") + + # Graph topology (for type=GRAPH) + routes: RouteDefinition | None = Field(default=None) + + # Memory and context + memory: MemoryConfig = Field(default_factory=MemoryConfig) + context: ContextConfigSchema = Field(default_factory=ContextConfigSchema) + + # Execution + timeout_seconds: int = Field(default=300, description="Total execution timeout") + max_iterations: int = Field(default=50, description="Max LLM calls per invocation") + + @model_validator(mode='after') + def validate_type_requirements(self) -> Self: + """Validate fields based on actor type.""" + if self.type == ActorType.LLM: + if not self.model: + raise ValueError("LLM actors require 'model' field") + if self.type == ActorType.GRAPH: + if not self.routes: + raise ValueError("GRAPH actors require 'routes' field") + return self + + model_config = ConfigDict(extra='forbid') # Reject unknown fields + ``` + - [ ] Commit: "feat(actor): define ActorConfigSchema with validation" + - [ ] **C1.4b** [Aditya] Add YAML loading helper: + ```python + @classmethod + def from_yaml(cls, path: Path | str) -> "ActorConfigSchema": + """Load and validate actor config from YAML file.""" + import yaml + with open(path) as f: + data = yaml.safe_load(f) + return cls.model_validate(data) + + def to_yaml(self) -> str: + """Serialize config to YAML string.""" + import yaml + return yaml.dump(self.model_dump(exclude_none=True), sort_keys=False) + ``` + - [ ] Commit: "feat(actor): add YAML serialization helpers" + - [ ] **C1.5** [Aditya] Create comprehensive example actors in `examples/actors/`: + - [ ] **C1.5a** [Aditya] Create `simple_llm_actor.yaml`: + ```yaml + version: "3" + name: simple-assistant + namespace: local + description: Basic LLM assistant with no tools + type: llm + model: gpt-4-turbo + provider: openai + system_prompt: | + You are a helpful coding assistant. Answer questions + concisely and provide code examples when appropriate. + temperature: 0.7 + memory: + enabled: true + max_turns: 10 + ``` + - [ ] Commit: "docs(examples): add simple_llm_actor.yaml" + - [ ] **C1.5b** [Aditya] Create `tool_actor.yaml`: + ```yaml + version: "3" + name: file-reader + namespace: local + description: Actor that can read and search files + type: llm + model: gpt-4-turbo + system_prompt: | + You can read and search files to answer questions. + tools: + - name: read_file + description: Read contents of a file + parameters: + - name: path + type: string + description: Path to file + code: | + result = context.get_file(input_data["path"]) + - name: search_files + description: Search for pattern in files + parameters: + - name: pattern + type: string + description: Regex pattern to search + code: | + result = context.search_files("**/*", input_data["pattern"]) + builtin_tools: + - list_directory + ``` + - [ ] Commit: "docs(examples): add tool_actor.yaml" + - [ ] **C1.5c** [Aditya] Create `graph_actor.yaml`: + ```yaml + version: "3" + name: research-writer + namespace: local + description: Multi-step research and writing workflow + type: graph + routes: + entry_point: planner + nodes: + - name: planner + type: agent + model: gpt-4-turbo + system_prompt: Break down the writing task into research topics. + - name: researcher + type: agent + model: gpt-4-turbo + system_prompt: Research the assigned topic thoroughly. + tools: [search_files, read_file] + - name: writer + type: agent + model: gpt-4-turbo + system_prompt: Write content based on research findings. + - name: router + type: conditional + edges: + - source: planner + target: router + - source: router + target: researcher + condition: "state.needs_research" + - source: router + target: writer + condition: "not state.needs_research" + - source: researcher + target: router + ``` + - [ ] Commit: "docs(examples): add graph_actor.yaml" + - [ ] **C1.5d** [Aditya] Create `hierarchical_actor.yaml`: + ```yaml + version: "3" + name: code-reviewer + namespace: local + description: Hierarchical actor that delegates to specialists + type: graph + routes: + entry_point: coordinator + nodes: + - name: coordinator + type: agent + model: gpt-4-turbo + system_prompt: | + Coordinate code review by delegating to specialists. + - name: security-check + type: subgraph + actor: local/security-analyzer + - name: style-check + type: subgraph + actor: local/style-checker + - name: aggregator + type: agent + model: gpt-4-turbo + system_prompt: Combine specialist feedback into final review. + edges: + - source: coordinator + target: security-check + - source: coordinator + target: style-check + - source: security-check + target: aggregator + - source: style-check + target: aggregator + ``` + - [ ] Commit: "docs(examples): add hierarchical_actor.yaml" + - [ ] **C1.5e** [Aditya] Create `strategy_actor.yaml`: + ```yaml + version: "3" + name: default-strategist + namespace: cleveragents + description: Default strategist for Strategize phase + type: llm + model: gpt-4-turbo + temperature: 0.3 # Lower for more consistent strategy + system_prompt: | + You are a technical strategist. Given a task description and codebase context, + create a detailed plan with specific steps. + + Your output must include: + 1. High-level approach explanation + 2. Ordered list of steps with file paths + 3. Dependencies between steps + 4. Risk assessment + 5. Decisions with alternatives considered + + Format decisions as: + DECISION: + CHOSEN: + ALTERNATIVES: + RATIONALE: + context: + context_view: strategist + include_file_patterns: + - "README.md" + - "**/README.md" + - "docs/**/*.md" + ``` + - [ ] Commit: "docs(examples): add strategy_actor.yaml" + - [ ] **C1.5f** [Aditya] Create `execution_actor.yaml`: + ```yaml + version: "3" + name: default-executor + namespace: cleveragents + description: Default executor for Execute phase + type: llm + model: gpt-4-turbo + temperature: 0.2 # Low for precise code generation + system_prompt: | + You are a code executor. Given a strategy and file context, + implement the required changes using the provided tools. + + RULES: + - Use tools to read files before editing + - Use edit_file for targeted changes, write_file for new files + - Always verify changes compile/parse correctly + - Document each change with clear commit messages + tools: + - name: edit_file + description: Make targeted edits to an existing file + parameters: + - name: path + type: string + - name: edits + type: array + code: | + result = context.edit_file(input_data["path"], input_data["edits"]) + builtin_tools: + - read_file + - write_file + - delete_file + - list_directory + - search_files + context: + context_view: executor + max_iterations: 100 # Allow more iterations for complex tasks + ``` + - [ ] Commit: "docs(examples): add execution_actor.yaml" + - [ ] **C1.6** [Aditya] Write comprehensive documentation: + - [ ] **C1.6a** [Aditya] Create `docs/reference/actor_configuration.md`: + - [ ] Full YAML schema reference with all fields + - [ ] Type-specific requirements (LLM vs GRAPH) + - [ ] Tool definition syntax and examples + - [ ] Memory and context configuration + - [ ] Commit: "docs: add actor configuration reference" + - [ ] **C1.6b** [Aditya] Add examples section: + - [ ] Example for each actor type + - [ ] Common patterns (research, review, generation) + - [ ] Anti-patterns to avoid + - [ ] Commit: "docs: add actor configuration examples" + - [ ] **C1.6c** [Aditya] Add migration guide: + - [ ] Changes from v2 format + - [ ] Automated migration script (if needed) + - [ ] Commit: "docs: add actor config migration guide" + - [ ] Tests: Behave scenarios for actor schema validation + - [ ] **C1.7** [Rui] Write Behave scenarios in `features/actor_schema.feature`: + - [ ] **C1.7a** [Rui] Valid config scenarios: + - [ ] Scenario: Simple LLM actor config validates successfully + - [ ] Scenario: Tool actor with inline code validates + - [ ] Scenario: Graph actor with complete topology validates + - [ ] Scenario: Actor with MCP servers configured validates + - [ ] Commit: "test(behave): add valid actor config scenarios" + - [ ] **C1.7b** [Rui] Invalid config scenarios: + - [ ] Scenario: LLM actor without model field fails + - [ ] Scenario: Graph actor without routes fails + - [ ] Scenario: Tool with invalid Python syntax fails + - [ ] Scenario: Graph with missing entry_point fails + - [ ] Scenario: Edge referencing non-existent node fails + - [ ] Commit: "test(behave): add invalid actor config scenarios" + - [ ] **C1.7c** [Rui] YAML loading scenarios: + - [ ] Scenario: Load actor config from YAML file + - [ ] Scenario: Invalid YAML syntax produces clear error + - [ ] Scenario: Unknown fields in YAML are rejected + - [ ] Commit: "test(behave): add YAML loading scenarios" -**Parallel Group C0.skill: Skill Registry & YAML [Aditya + Jeff + Luis + Rui]** (depends on C0.domain + C0.registry; must land before C3.protocol) - **PARALLEL SUBTRACK C0.skill.schema [Aditya]**: Skill YAML schema + examples - **PARALLEL SUBTRACK C0.skill.domain [Jeff]**: Skill domain model + resolver - **PARALLEL SUBTRACK C0.skill.registry [Luis]**: Skill persistence + service - **PARALLEL SUBTRACK C0.skill.cli [Rui]**: CLI commands + output formatting - **SEQUENTIAL MERGE NOTE**: C0.skill.domain must land before C0.skill.registry/C0.skill.cli to avoid dual representations. -- [ ] **COMMIT (Owner: Aditya | Group: C0.skill.schema) - Commit message: "docs(skill): add skill yaml schema and examples"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Docs [Aditya]: Author `docs/schema/skill.schema.yaml` with versioning, required fields, and explicit type constraints for tool refs, inline tools, includes, and MCP sources. - - [ ] Docs [Aditya]: Add skill YAML examples under `examples/skills/` (single-tool, composed, inline tool, validation-only, MCP-backed). - - [ ] Code [Aditya]: Add schema loader in `src/cleveragents/skills/schema.py` that validates schema version, normalizes keys, and returns typed data. - - [ ] Code [Aditya]: Add clear validation errors for missing tools, recursive includes, and invalid namespaced names. - - [ ] Tests (Behave) [Aditya]: Add `features/skill_schema.feature` scenarios validating each example and invalid cases. - - [ ] Tests (Robot) [Aditya]: Add `robot/skill_schema.robot` to load and validate every example. - - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/skill_schema_bench.py` for schema validation throughput. - - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Aditya]: `git commit -m "docs(skill): add skill yaml schema and examples"`. -- [ ] **COMMIT (Owner: Jeff | Group: C0.skill.domain) - Commit message: "feat(skill): add skill domain model and resolver"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add `Skill`, `SkillItem`, `SkillToolRef`, `SkillInclude`, and `SkillInlineTool` models in `src/cleveragents/domain/models/core/skill.py` with namespaced naming rules. - - [ ] Code [Jeff]: Implement `SkillResolver` to flatten includes into ordered tool lists, de-duplicate tools, and reject cycles with path traces. - - [ ] Code [Jeff]: Add `Skill.resolve_tools()` returning resolved tool/validation names plus inline tool definitions for compiler use. - - [ ] Docs [Jeff]: Add `docs/reference/skill_model.md` and `docs/reference/skill_resolution.md` with resolution order examples. - - [ ] Tests (Behave) [Jeff]: Add `features/skill_resolution.feature` for include ordering, de-dupe rules, and cycle detection. - - [ ] Tests (Robot) [Jeff]: Add `robot/skill_resolution.robot` smoke tests for resolver output. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/skill_resolution_bench.py` for resolver performance. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(skill): add skill domain model and resolver"`. -- [ ] **COMMIT (Owner: Luis | Group: C0.skill.registry) - Commit message: "feat(skill): add skill registry persistence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add `skills` and `skill_items` tables (namespaced name PK, description, source, yaml_text, timestamps) with indexes on namespace/name. - - [ ] Code [Luis]: Implement `SkillRepository` CRUD + list filters and `SkillRegistryService` with add/update/remove/show/list. - - [ ] Code [Luis]: Enforce referential integrity for included skills and tool references at registration time. - - [ ] Docs [Luis]: Add `docs/reference/skill_registry.md` with registration and update behavior. - - [ ] Tests (Behave) [Luis]: Add `features/skill_registry.feature` for add/update/remove and invalid include cases. - - [ ] Tests (Robot) [Luis]: Add `robot/skill_registry.robot` CLI/service smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/skill_registry_bench.py` for registry list performance. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(skill): add skill registry persistence"`. -- [ ] **COMMIT (Owner: Rui | Group: C0.skill.cli) - Commit message: "feat(cli): add skill commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Rui]: Implement `agents skill add/remove/list/show/tools` with YAML config input and `--namespace` filter. - - [ ] Code [Rui]: Ensure `skill tools` shows resolved tool list, inline tool IDs, and validation nodes. - - [ ] Docs [Rui]: Update CLI reference with skill commands, examples, and output fields. - - [ ] Tests (Behave) [Rui]: Add CLI scenarios for skill add/show/tools/list/remove. - - [ ] Tests (Robot) [Rui]: Add `robot/skill_cli.robot` for end-to-end CLI flows. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/skill_cli_bench.py` for config parsing overhead. - - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "feat(cli): add skill commands"`. +- [ ] **Stage C2: Actor Loading & Compilation** (Day 6-8) **[Aditya]** + + **SEQUENTIAL ORDER**: C2.1 (Config parser) → C2.2 (CompiledActor) → C2.3 (LLM compiler) → C2.4 (Tool compiler) → C2.5 (Graph compiler) → C2.6 (Reference resolution) → C2.7 (Registry) → C2.8 (Tests) + + - [ ] Code: Enhance actor loading and compilation to LangGraph + - [ ] **C2.1** [Aditya] Create config parser in `src/cleveragents/actor/config.py`: + - [ ] **C2.1a** [Aditya] Define `ActorConfigParser` class: + ```python + class ActorConfigParser: + """Parse and validate actor configurations.""" + + def __init__(self, registry: "ActorRegistry"): + self._registry = registry + + def parse_file(self, path: Path) -> ActorConfigSchema: + """Parse actor config from YAML file.""" + return ActorConfigSchema.from_yaml(path) + + def parse_string(self, content: str) -> ActorConfigSchema: + """Parse actor config from YAML string.""" + import yaml + data = yaml.safe_load(content) + return ActorConfigSchema.model_validate(data) + ``` + - [ ] Commit: "feat(actor): add ActorConfigParser scaffold" + - [ ] **C2.1b** [Aditya] Add tools section validation: + - [ ] Validate each tool has unique name + - [ ] Validate tool code compiles (syntax check) + - [ ] Validate tool parameters have valid JSON Schema types + - [ ] Commit: "feat(actor): add tools section validation" + - [ ] **C2.1c** [Aditya] Add routes section validation: + - [ ] Validate all node names are unique + - [ ] Validate entry_point exists in nodes + - [ ] Validate all edge sources/targets exist + - [ ] Check for unreachable nodes (warning) + - [ ] Commit: "feat(actor): add routes section validation" + - [ ] **C2.1d** [Aditya] Add actor reference validation: + - [ ] For subgraph nodes, validate `actor` field is present + - [ ] Validate referenced actor exists in registry + - [ ] Build dependency graph for circular reference detection + - [ ] Commit: "feat(actor): add actor reference validation" + - [ ] **C2.2** [Aditya] Define `CompiledActor` in `src/cleveragents/actor/compiled.py`: + - [ ] **C2.2a** [Aditya] Create CompiledActor dataclass: + ```python + @dataclass + class CompiledActor: + """A compiled actor ready for execution.""" + config: ActorConfigSchema + graph: StateGraph # LangGraph StateGraph + runnable: CompiledStateGraph # Compiled version for execution + tools: dict[str, Callable] # Name -> callable tool functions + referenced_actors: list[str] # Actor names this depends on + compiled_at: datetime + + def invoke(self, input_data: dict, config: RunnableConfig | None = None) -> dict: + """Execute the actor graph with input.""" + return self.runnable.invoke(input_data, config) + + async def ainvoke(self, input_data: dict, config: RunnableConfig | None = None) -> dict: + """Execute the actor graph asynchronously.""" + return await self.runnable.ainvoke(input_data, config) + ``` + - [ ] Commit: "feat(actor): define CompiledActor dataclass" + - [ ] **C2.3** [Aditya] Create `ActorCompiler` in `src/cleveragents/actor/compiler.py`: + - [ ] **C2.3a** [Aditya] Define compiler class scaffold: + ```python + class ActorCompiler: + """Compile actor configs into executable LangGraph graphs.""" + + def __init__( + self, + model_factory: ModelFactory, + skill_registry: SkillRegistry, + mcp_manager: MCPServerManager | None = None + ): + self._model_factory = model_factory + self._skill_registry = skill_registry + self._mcp_manager = mcp_manager + self._compiled_cache: dict[str, CompiledActor] = {} + ``` + - [ ] Commit: "feat(actor): add ActorCompiler scaffold" + - [ ] **C2.3b** [Aditya] Implement main `compile()` method: + ```python + def compile(self, config: ActorConfigSchema) -> CompiledActor: + """Compile actor config into executable graph.""" + # Check cache + cache_key = f"{config.namespace}/{config.name}" + if cache_key in self._compiled_cache: + return self._compiled_cache[cache_key] + + # Compile based on type + match config.type: + case ActorType.LLM: + graph = self._compile_llm_actor(config) + case ActorType.TOOL: + graph = self._compile_tool_actor(config) + case ActorType.GRAPH: + graph = self._compile_graph_actor(config) + + # Build tools dict + tools = self._build_tools(config) + + # Create CompiledActor + compiled = CompiledActor( + config=config, + graph=graph, + runnable=graph.compile(), + tools=tools, + referenced_actors=self._get_referenced_actors(config), + compiled_at=datetime.utcnow() + ) + + # Cache and return + self._compiled_cache[cache_key] = compiled + return compiled + ``` + - [ ] Commit: "feat(actor): implement ActorCompiler.compile()" + - [ ] **C2.4** [Aditya] Implement LLM actor compilation: + - [ ] **C2.4a** [Aditya] Implement `_compile_llm_actor()`: + ```python + def _compile_llm_actor(self, config: ActorConfigSchema) -> StateGraph: + """Compile simple LLM actor into single-node graph.""" + from langgraph.graph import StateGraph, END + from langchain_core.messages import HumanMessage, SystemMessage + + # Create model + model = self._model_factory.create( + model_name=config.model, + provider=config.provider, + temperature=config.temperature, + max_tokens=config.max_tokens + ) + + # Bind tools if any + tools = self._build_tools(config) + if tools: + model = model.bind_tools(list(tools.values())) + + # Define state + class State(TypedDict): + messages: list[BaseMessage] + context: dict + + # Define agent node + def agent(state: State) -> State: + messages = state["messages"] + if config.system_prompt: + messages = [SystemMessage(content=config.system_prompt)] + messages + response = model.invoke(messages) + return {"messages": [response]} + + # Build graph + graph = StateGraph(State) + graph.add_node("agent", agent) + graph.set_entry_point("agent") + graph.add_edge("agent", END) + + return graph + ``` + - [ ] Commit: "feat(actor): implement LLM actor compilation" + - [ ] **C2.4b** [Aditya] Add memory support to LLM actors: + - [ ] If config.memory.enabled, wrap with memory checkpointer + - [ ] Configure message trimming based on max_turns + - [ ] Commit: "feat(actor): add memory support to LLM actors" + - [ ] **C2.5** [Aditya] Implement tool actor compilation: + - [ ] **C2.5a** [Aditya] Implement `_compile_tool_actor()`: + - [ ] Create ReAct-style agent with tools + - [ ] Configure tool calling loop + - [ ] Add tool nodes for each defined tool + - [ ] Commit: "feat(actor): implement tool actor compilation" + - [ ] **C2.5b** [Aditya] Implement tool node generation: + ```python + def _build_tools(self, config: ActorConfigSchema) -> dict[str, Callable]: + """Build callable tools from config.""" + tools = {} + + # Inline tools from YAML + for tool_def in config.tools: + tools[tool_def.name] = self._create_inline_tool(tool_def) + + # Built-in tools + for tool_name in config.builtin_tools: + tool = self._skill_registry.get_skill(tool_name) + if tool: + tools[tool_name] = tool.to_langchain_tool() + + # MCP tools + if config.mcp_servers and self._mcp_manager: + for server_id in config.mcp_servers: + mcp_tools = self._mcp_manager.get_tools(server_id) + tools.update(mcp_tools) + + return tools + ``` + - [ ] Commit: "feat(actor): implement tool building from config" + - [ ] **C2.6** [Aditya] Implement graph actor compilation: + - [ ] **C2.6a** [Aditya] Implement `_compile_graph_actor()`: + ```python + def _compile_graph_actor(self, config: ActorConfigSchema) -> StateGraph: + """Compile multi-node graph actor.""" + routes = config.routes + + # Define state type dynamically based on nodes + State = self._build_state_type(routes) + + # Create graph + graph = StateGraph(State) + + # Add nodes + for node_def in routes.nodes: + node_func = self._create_node(node_def, config) + graph.add_node(node_def.name, node_func) + + # Set entry point + graph.set_entry_point(routes.entry_point) + + # Add edges + for edge in routes.edges: + if edge.condition: + # Conditional edge + condition_func = self._parse_condition(edge.condition) + graph.add_conditional_edges( + edge.source, + condition_func, + {True: edge.target} + ) + else: + # Direct edge + graph.add_edge(edge.source, edge.target) + + return graph + ``` + - [ ] Commit: "feat(actor): implement graph actor compilation" + - [ ] **C2.6b** [Aditya] Implement node creation by type: + - [ ] Agent nodes: create LLM with optional tools + - [ ] Tool nodes: create tool execution wrapper + - [ ] Conditional nodes: create routing logic + - [ ] Subgraph nodes: compile and embed referenced actor + - [ ] Commit: "feat(actor): implement node creation by type" + - [ ] **C2.7** [Aditya] Implement actor reference resolution: + - [ ] **C2.7a** [Aditya] Add circular reference detection: + ```python + def _check_circular_references( + self, + config: ActorConfigSchema, + visited: set[str] | None = None + ) -> None: + """Detect circular actor references.""" + visited = visited or set() + actor_name = f"{config.namespace}/{config.name}" + + if actor_name in visited: + raise CircularReferenceError( + f"Circular reference detected: {' -> '.join(visited)} -> {actor_name}" + ) + + visited.add(actor_name) + + for ref in self._get_referenced_actors(config): + ref_config = self._registry.get_config(ref) + if ref_config: + self._check_circular_references(ref_config, visited.copy()) + ``` + - [ ] Commit: "feat(actor): add circular reference detection" + - [ ] **C2.7b** [Aditya] Implement recursive compilation: + - [ ] When compiling subgraph node, recursively compile referenced actor + - [ ] Cache compiled actors to avoid recompilation + - [ ] Pass context appropriately to subgraphs + - [ ] Commit: "feat(actor): implement recursive actor compilation" + - [ ] **C2.8** [Aditya] Update `ActorRegistry` to support compilation: + - [ ] **C2.8a** [Aditya] Add `get_compiled()` method: + ```python + def get_compiled(self, name: str) -> CompiledActor: + """Get compiled actor by name, compiling if needed.""" + # Check compiled cache + if name in self._compiled_cache: + cached = self._compiled_cache[name] + # Check if config changed + current_config = self.get_config(name) + if current_config and self._config_unchanged(name, current_config): + return cached + + # Load config + config = self.get_config(name) + if not config: + raise ActorNotFoundError(f"Actor '{name}' not found") + + # Compile + compiled = self._compiler.compile(config) + self._compiled_cache[name] = compiled + + return compiled + ``` + - [ ] Commit: "feat(actor): add ActorRegistry.get_compiled()" + - [ ] **C2.8b** [Aditya] Add cache invalidation: + - [ ] Monitor actor YAML files for changes + - [ ] Clear cache entry when config file modified + - [ ] Clear dependent actors when base actor changes + - [ ] Commit: "feat(actor): add compiled actor cache invalidation" + - [ ] Tests: Behave scenarios for actor compilation + - [ ] **C2.9** [Rui] Write Behave scenarios in `features/actor_compilation.feature`: + - [ ] **C2.9a** [Rui] LLM actor compilation scenarios: + - [ ] Scenario: Compile simple LLM actor creates valid graph + - [ ] Given actor config with type=llm and model=gpt-4 + - [ ] When I compile the actor + - [ ] Then CompiledActor is returned + - [ ] And graph has single agent node + - [ ] And runnable can be invoked + - [ ] Scenario: LLM actor with tools binds tools correctly + - [ ] Commit: "test(behave): add LLM actor compilation scenarios" + - [ ] **C2.9b** [Rui] Tool actor compilation scenarios: + - [ ] Scenario: Compile tool actor with inline code works + - [ ] Given actor config with inline tool definitions + - [ ] When I compile the actor + - [ ] Then tools dict contains the defined tools + - [ ] And tools are callable + - [ ] Scenario: Built-in tools are included + - [ ] Commit: "test(behave): add tool actor compilation scenarios" + - [ ] **C2.9c** [Rui] Graph actor compilation scenarios: + - [ ] Scenario: Compile graph actor creates correct topology + - [ ] Given actor config with routes defining 3 nodes + - [ ] When I compile the actor + - [ ] Then graph has 3 nodes + - [ ] And edges match route definition + - [ ] And entry_point is set correctly + - [ ] Scenario: Conditional edges work correctly + - [ ] Commit: "test(behave): add graph actor compilation scenarios" + - [ ] **C2.9d** [Rui] Reference resolution scenarios: + - [ ] Scenario: Actor referencing other actor compiles recursively + - [ ] Given actor A references actor B as subgraph + - [ ] When I compile actor A + - [ ] Then actor B is also compiled + - [ ] And actor B graph is embedded in actor A + - [ ] Scenario: Circular reference detected and errors + - [ ] Given actor A references B and B references A + - [ ] When I try to compile actor A + - [ ] Then CircularReferenceError is raised + - [ ] And error message shows the cycle + - [ ] Commit: "test(behave): add reference resolution scenarios" + - [ ] **C2.9e** [Rui] Error scenarios: + - [ ] Scenario: Invalid actor config produces clear error + - [ ] Scenario: Missing referenced actor produces clear error + - [ ] Scenario: Invalid tool code produces clear error + - [ ] Commit: "test(behave): add compilation error scenarios" -**Parallel Group C0.runtime: Tool Lifecycle Runtime [Jeff]** (depends on C0.domain + C0.registry; must land before C3.context) -- [ ] **COMMIT (Owner: Jeff | Group: C0.runtime) - Commit message: "feat(tool): add tool lifecycle runtime"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Implement `ToolRuntime`/`ToolInstance` interfaces with `discover/activate/execute/deactivate` hooks and lifecycle state tracking. - - [ ] Code [Jeff]: Add `ToolExecutionContext` with resolved resource bindings, sandbox paths, plan metadata, and cancellation token. - - [ ] Code [Jeff]: Add lifecycle cache with per-plan activation reuse and guaranteed `deactivate` on plan completion/cancel. - - [ ] Code [Jeff]: Enforce tool capability flags (read-only/writes/checkpointable) and read-only plan gating at runtime. - - [ ] Docs [Jeff]: Add `docs/reference/tool_lifecycle.md` describing hook ordering and failure handling. - - [ ] Tests (Behave) [Jeff]: Add lifecycle scenarios for activate/execute/deactivate ordering and error propagation. - - [ ] Tests (Robot) [Jeff]: Add `robot/tool_lifecycle.robot` runtime smoke tests. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/tool_lifecycle_bench.py` for lifecycle overhead. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(tool): add tool lifecycle runtime"`. +- [ ] **Stage C3: Skill Execution Framework** (Day 5-6) **[Jeff + Aditya - Critical Path]** + - [ ] Code: Implement skill execution framework + - [ ] **C3.1** [Jeff] Define `Skill` protocol in `src/cleveragents/actor/skills/protocol.py`: + - [ ] **C3.1a** Create abstract base using `typing.Protocol`: + ```python + class Skill(Protocol): + @property + def name(self) -> str: ... + @property + def description(self) -> str: ... + @property + def parameters(self) -> dict[str, Any]: ... # JSON Schema + @property + def metadata(self) -> SkillMetadata: ... + + async def execute( + self, + input_data: dict[str, Any], + context: SkillContext + ) -> SkillResult: ... + ``` + - [ ] **C3.1b** Define `SkillResult` dataclass: + - [ ] Field `success: bool` - whether execution succeeded + - [ ] Field `result: Any` - return value if successful + - [ ] Field `error: str | None` - error message if failed + - [ ] Field `changes: list[Change]` - changes made to resources + - [ ] Field `duration_ms: int` - execution time + - [ ] **C3.1c** Add docstrings explaining contract for skill implementers + - [ ] **C3.2** [Jeff] Define `SkillMetadata` Pydantic model in `src/cleveragents/actor/skills/metadata.py`: + - [ ] **C3.2a** Core capability fields: + - [ ] Field `read_only: bool = False` - only performs read operations + - [ ] Field `writes: bool = False` - can modify resources + - [ ] Field `write_scope: list[str] = []` - glob patterns for writable paths + - [ ] Field `idempotent: bool = False` - repeated calls produce same result + - [ ] Field `checkpointable: bool = False` - supports checkpoint/rollback + - [ ] Field `side_effects: list[str] = []` - external side effects (e.g., "network", "subprocess") + - [ ] **C3.2b** Safety and control fields: + - [ ] Field `human_approval_required: bool = False` - requires user confirmation + - [ ] Field `rate_limit: RateLimit | None = None` - calls per minute/hour + - [ ] Field `cost_profile: CostProfile | None = None` - estimated cost per call + - [ ] Field `timeout_seconds: int = 30` - maximum execution time + - [ ] **C3.2c** Define `RateLimit` and `CostProfile` models + - [ ] **C3.2d** Add validation to ensure `writes=True` if `write_scope` is non-empty + - [ ] **C3.3** [Jeff] Create `SkillContext` in `src/cleveragents/actor/skills/context.py`: + - [ ] **C3.3a** Define context fields: + - [ ] Field `plan_id: str` - current plan ULID + - [ ] Field `plan: Plan` - full plan object for reference + - [ ] Field `project: Project` - target project + - [ ] Field `resources: list[Resource]` - available resources + - [ ] Field `sandbox_manager: SandboxManager` - for sandbox access + - [ ] Field `changeset: ChangeSet` - accumulating changes + - [ ] Field `invocation_tracker: SkillInvocationTracker` - tracking calls + - [ ] Field `logger: logging.Logger` - skill-specific logger + - [ ] **C3.3b** Implement convenience methods: + - [ ] Method `get_file(path: str, resource: str | None = None) -> str`: + - [ ] Resolve path to sandboxed location + - [ ] Read and return file contents + - [ ] Raise FileNotFoundError if not exists + - [ ] Method `write_file(path: str, content: str, resource: str | None = None) -> Change`: + - [ ] Resolve path to sandboxed location + - [ ] Validate path against deny-list + - [ ] Create parent directories if needed + - [ ] Write content + - [ ] Create and record Change + - [ ] Return Change for tracking + - [ ] Method `edit_file(path: str, edits: list[Edit], resource: str | None = None) -> Change`: + - [ ] Read current content + - [ ] Apply edits sequentially + - [ ] Write modified content + - [ ] Record Change with edits + - [ ] Method `delete_file(path: str, resource: str | None = None) -> Change`: + - [ ] Validate file exists + - [ ] Delete file + - [ ] Record Change + - [ ] Method `list_files(pattern: str, resource: str | None = None) -> list[str]`: + - [ ] Resolve pattern to sandbox + - [ ] Return matching paths + - [ ] Method `search_files(pattern: str, content_pattern: str, resource: str | None = None) -> list[SearchResult]`: + - [ ] Search file contents with regex + - [ ] Return matches with file, line, context + - [ ] **C3.3c** Implement subplan spawning: + - [ ] Method `spawn_subplan(action: str, target_resources: list[str] | None = None, arguments: dict | None = None) -> str`: + - [ ] Validate action exists + - [ ] Create child plan with parent_plan_id = self.plan_id + - [ ] Queue subplan for execution + - [ ] Return subplan_id for tracking + - [ ] Record as `subplan_spawn` decision type + - [ ] **C3.3d** Implement read-only check enforcement: + - [ ] If plan.action.read_only is True, block all write operations + - [ ] Raise `ReadOnlyViolationError` if write attempted + - [ ] **C3.4** [Aditya] Implement `InlineSkillExecutor` in `src/cleveragents/actor/skills/inline_executor.py`: + - [ ] **C3.4a** Create class for executing inline Python code from actor YAML: + ```python + class InlineSkillExecutor: + def __init__(self, code: str, timeout: int = 30): + self.code = code + self.timeout = timeout + ``` + - [ ] **C3.4b** Create sandboxed execution environment: + - [ ] Restricted `__builtins__`: + - [ ] ALLOWED: `len`, `range`, `str`, `int`, `float`, `list`, `dict`, `set`, `tuple`, `bool`, `None`, `True`, `False`, `print`, `isinstance`, `hasattr`, `getattr`, `enumerate`, `zip`, `map`, `filter`, `sorted`, `reversed`, `any`, `all`, `min`, `max`, `sum`, `abs`, `round` + - [ ] BLOCKED: `open`, `exec`, `eval`, `compile`, `__import__`, `globals`, `locals`, `vars`, `dir`, `input` + - [ ] Inject `context: SkillContext` variable + - [ ] Inject `input_data: dict` variable + - [ ] Inject standard library modules: `re`, `json`, `datetime`, `collections`, `itertools`, `functools` + - [ ] **C3.4c** Execute code and capture result: + - [ ] Use `exec()` with restricted globals/locals + - [ ] Capture `result` variable as return value + - [ ] If no `result` variable, return None + - [ ] Wrap in asyncio.wait_for for timeout + - [ ] **C3.4d** Handle errors gracefully: + - [ ] Catch all exceptions during execution + - [ ] Convert to SkillResult with error message + - [ ] Include stack trace in error for debugging + - [ ] Log error with skill name and input + - [ ] **C3.4e** Add timeout support: + - [ ] Default 30 seconds + - [ ] Configurable via skill metadata + - [ ] Raise TimeoutError if exceeded + - [ ] **C3.5** [Aditya] Implement subplan spawning in skill context: + - [ ] **C3.5a** Update SkillContext.spawn_subplan to create real subplans: + - [ ] Call PlanLifecycleService.use_action() with parent_plan_id + - [ ] Set subplan's root_plan_id to parent's root_plan_id (or parent's id if root) + - [ ] Set subplan's automation_level from parent + - [ ] Return subplan_id + - [ ] **C3.5b** Add subplan tracking to parent plan: + - [ ] Store spawned subplan IDs in plan's execution_log + - [ ] Support querying all subplans of a plan + - [ ] **C3.5c** Add subplan completion handling: + - [ ] Parent plan can check subplan status + - [ ] Parent plan can collect subplan results + - [ ] Support waiting for subplan completion + - [ ] **C3.6** [Jeff + Luis] Implement built-in resource skills in `src/cleveragents/actor/skills/builtin/`: + - [ ] **C3.6a** [Jeff] Create base skill class in `__init__.py`: + ```python + class BuiltinSkill(ABC): + @abstractmethod + async def execute(self, input_data: dict, context: SkillContext) -> SkillResult: ... + + def _validate_path(self, path: str, context: SkillContext) -> str: + """Resolve and validate path against sandbox.""" + ... + ``` + - [ ] **C3.6b** [Jeff] File operation skills in `file_ops.py`: + - [ ] **ReadFileSkill**: + - [ ] Parameters: `path: str` (required) + - [ ] Metadata: `read_only=True` + - [ ] Implementation: resolve path, read via sandbox, return content + - [ ] Error handling: FileNotFoundError, PermissionError + - [ ] **WriteFileSkill**: + - [ ] Parameters: `path: str`, `content: str` + - [ ] Metadata: `writes=True, write_scope=['**/*']` + - [ ] Implementation: + - [ ] Validate path not in deny-list + - [ ] Create parent directories if needed + - [ ] Determine if create or modify operation + - [ ] Write content to sandbox + - [ ] Create Change record with operation type + - [ ] Record change in context.changeset + - [ ] Return: Change object with path and operation + - [ ] **EditFileSkill** (most complex - critical for coding): + - [ ] Parameters: `path: str`, `edits: list[Edit]` + - [ ] Metadata: `writes=True, idempotent=False` + - [ ] Implementation: + - [ ] Read original file content + - [ ] For each Edit in edits: + - [ ] If type=SEARCH_REPLACE: find `search` text, replace with `replace` + - [ ] If type=LINE_RANGE: replace lines start_line:end_line with content + - [ ] If type=INSERT_AFTER: insert content after matching line + - [ ] If type=INSERT_BEFORE: insert content before matching line + - [ ] If type=DELETE_LINES: remove lines start_line:end_line + - [ ] Track all changes made + - [ ] Write modified content + - [ ] Create Change with edits list + - [ ] Error handling: SearchTextNotFoundError, InvalidLineRangeError + - [ ] **DeleteFileSkill**: + - [ ] Parameters: `path: str` + - [ ] Metadata: `writes=True` + - [ ] Implementation: delete file, create DELETE Change + - [ ] **MoveFileSkill**: + - [ ] Parameters: `source: str`, `destination: str` + - [ ] Metadata: `writes=True` + - [ ] Implementation: move file, create MOVE Change with new_path + - [ ] **CopyFileSkill**: + - [ ] Parameters: `source: str`, `destination: str` + - [ ] Metadata: `writes=True` + - [ ] Implementation: copy file, create CREATE Change + - [ ] **C3.6c** [Luis] Directory operation skills in `dir_ops.py`: + - [ ] **CreateDirectorySkill**: + - [ ] Parameters: `path: str` + - [ ] Metadata: `writes=True` + - [ ] Implementation: create directory (and parents), record Change + - [ ] **ListDirectorySkill**: + - [ ] Parameters: `path: str`, `pattern: str = "*"`, `recursive: bool = False` + - [ ] Metadata: `read_only=True` + - [ ] Implementation: list matching files/dirs, return paths + - [ ] **DeleteDirectorySkill**: + - [ ] Parameters: `path: str`, `recursive: bool = False` + - [ ] Metadata: `writes=True` + - [ ] Implementation: delete directory, record DELETE Changes for all contents + - [ ] **C3.6d** [Luis] Search skills in `search_ops.py`: + - [ ] **SearchFilesSkill**: + - [ ] Parameters: `pattern: str` (glob), `content_pattern: str` (regex), `max_results: int = 100` + - [ ] Metadata: `read_only=True` + - [ ] Implementation: + - [ ] Find files matching glob pattern + - [ ] Search each file for content_pattern + - [ ] Return list of SearchResult(file, line_number, line_content, context) + - [ ] **FindDefinitionSkill** (uses tree-sitter for AST): + - [ ] Parameters: `symbol: str`, `language: str | None = None` + - [ ] Metadata: `read_only=True` + - [ ] Implementation: + - [ ] Parse files with tree-sitter + - [ ] Find function/class/variable definitions + - [ ] Return list of Location(file, line, column, snippet) + - [ ] **FindReferencesSkill**: + - [ ] Parameters: `symbol: str` + - [ ] Metadata: `read_only=True` + - [ ] Implementation: find all usages of symbol + - [ ] **GetFileInfoSkill**: + - [ ] Parameters: `path: str` + - [ ] Metadata: `read_only=True` + - [ ] Implementation: return FileInfo(size, mtime, language, line_count) + - [ ] **C3.6e** [Hamza] Git operation skills in `git_ops.py`: + - [ ] **GitStatusSkill**: + - [ ] Parameters: (none) + - [ ] Metadata: `read_only=True` + - [ ] Implementation: run `git status --porcelain`, parse output + - [ ] **GitDiffSkill**: + - [ ] Parameters: `path: str | None = None`, `staged: bool = False` + - [ ] Metadata: `read_only=True` + - [ ] Implementation: run `git diff [--staged] [path]` + - [ ] **GitLogSkill**: + - [ ] Parameters: `count: int = 10`, `path: str | None = None` + - [ ] Metadata: `read_only=True` + - [ ] Implementation: run `git log --oneline -n {count}` + - [ ] **GitBlameSkill**: + - [ ] Parameters: `path: str`, `start_line: int | None`, `end_line: int | None` + - [ ] Metadata: `read_only=True` + - [ ] Implementation: run `git blame -L {start},{end} {path}` + - [ ] **C3.6f** [Jeff] All built-in skills must implement: + - [ ] Full SkillMetadata with accurate capability flags + - [ ] Proper error handling with descriptive messages + - [ ] Logging of all operations for debugging + - [ ] Path validation against sandbox and deny-lists + - [ ] Change recording for all write operations + - [ ] Async execution support + - [ ] **C3.6g** [Jeff] Create skill registration in `src/cleveragents/actor/skills/registry.py`: + - [ ] `BuiltinSkillRegistry` singleton with all built-in skills + - [ ] Method `get_skill(name: str) -> Skill | None` + - [ ] Method `list_skills() -> list[Skill]` + - [ ] Method `list_by_capability(read_only: bool = None, writes: bool = None) -> list[Skill]` + - [ ] Register all C3.6 skills on import + - [ ] **C3.7** [Aditya] Implement MCP skill adapter in `src/cleveragents/actor/skills/mcp_adapter.py`: + - [ ] **C3.7a** `MCPServerConnection` class: + - [ ] Connect to MCP server via stdio or SSE transport + - [ ] List available tools from server + - [ ] Call tools with JSON-RPC + - [ ] Handle server lifecycle (start/stop) + - [ ] **C3.7b** `MCPSkillAdapter` class: + - [ ] Wrap MCP tool as CleverAgents Skill + - [ ] Infer SkillMetadata from MCP tool schema + - [ ] Intercept calls for sandbox path rewriting + - [ ] Record changes when MCP tool modifies resources + - [ ] **C3.7c** Actor YAML integration: + - [ ] Parse `mcp_servers` config in actor definition + - [ ] Auto-register MCP tools as skills in actor context + - [ ] Environment variable substitution for secrets + - [ ] Tests: Integration tests for skill execution + - [ ] **C3.8** [Rui] Write Behave scenarios in `features/skill_execution.feature`: + - [ ] Scenario: Execute inline Python skill with context + - [ ] Scenario: Skill can read files from sandbox + - [ ] Scenario: Skill can write files to sandbox + - [ ] Scenario: Skill with invalid code produces error + - [ ] Scenario: Skill timeout prevents infinite loops + - [ ] Scenario: spawn_subplan creates child plan + - [ ] **C3.9** [Rui] Write Behave scenarios in `features/builtin_skills.feature`: + - [ ] Scenario: WriteFileSkill creates file and records Change + - [ ] Scenario: EditFileSkill applies search/replace edit + - [ ] Scenario: DeleteFileSkill removes file and records Change + - [ ] Scenario: MoveFileSkill renames file and records Change + - [ ] Scenario: ListDirectorySkill returns matching files + - [ ] Scenario: SearchFilesSkill finds content matches + - [ ] Scenario: Skill respects deny-list patterns (.git/, node_modules/) + - [ ] Scenario: Skill enforces sandbox boundaries + - [ ] **C3.10** [Rui] Write Behave scenarios in `features/mcp_integration.feature`: + - [ ] Scenario: Connect to MCP server and list tools + - [ ] Scenario: MCP tool becomes available as skill + - [ ] Scenario: MCP tool call is intercepted for sandbox paths + - [ ] Scenario: MCP tool writes are recorded in ChangeSet -**Parallel Group C1: Actor Schema & Examples [Aditya + Jeff]** (start Day 5; C2 depends on this) -- [ ] **COMMIT (Owner: Aditya | Group: C1.schema) - Commit message: "feat(actor): add actor yaml schema models"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Aditya]: Add schema models (ActorType, NodeType, ContextView, ToolDefinition, RouteDefinition, ActorConfigSchema) with strict validation in `src/cleveragents/actor/schema.py`. - - [ ] Code [Aditya]: Add tool-node schema fields that reference Tool Registry names, including validation nodes, and require input/output schema presence. - - [ ] Code [Aditya]: Add YAML load/serialize helpers and schema version guard. - - [ ] Docs [Aditya]: Add `docs/reference/actors_schema.md` with field definitions, tool node semantics, and graph constraints. - - [ ] Tests (Behave) [Aditya]: Add `features/actor_schema.feature` scenarios for validation and topology errors. - - [ ] Tests (Robot) [Aditya]: Add `robot/actor_schema.robot` YAML load smoke test. - - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/actor_schema_bench.py` for YAML validation cost. - - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Aditya]: `git commit -m "feat(actor): add actor yaml schema models"`. -- [ ] **COMMIT (Owner: Aditya | Group: C1.examples) - Commit message: "docs(actor): add actor yaml examples"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Docs [Aditya]: Add `docs/reference/actors_examples.md` with strategist, executor, reviewer, tool-only, validation-node, and graph YAML examples. - - [ ] Docs [Aditya]: Store example YAML files under `examples/actors/` for automated tests. - - [ ] Tests (Behave) [Aditya]: Add `features/actor_examples.feature` to ensure all examples validate. - - [ ] Tests (Robot) [Aditya]: Add `robot/actor_examples.robot` to load each example. - - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/actor_examples_load_bench.py` for YAML load throughput. - - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Aditya]: `git commit -m "docs(actor): add actor yaml examples"`. +- [ ] **Stage C4: Tool-Based Change Tracking** (Day 8-10) **[Luis - Architectural]** + - [ ] Code: Implement tool-based change tracking (NOT output parsing) + - [ ] **CRITICAL ARCHITECTURE**: ChangeSet is built from skill/tool invocations, NOT by parsing LLM text output + - [ ] **C4.1** [Luis] Update `src/cleveragents/domain/models/core/change.py`: + - [ ] Enhance `Change` model with: + - [ ] Field `operation: OperationType` - create/modify/delete/move + - [ ] Field `path: str` - target resource path + - [ ] Field `new_path: str | None` - for move operations + - [ ] Field `content: str | None` - full content (for create) + - [ ] Field `edits: list[Edit] | None` - targeted edits (for modify) + - [ ] Field `patch: str | None` - unified diff (generated, not parsed) + - [ ] Field `language: str | None` - detected language + - [ ] Field `skill_invocation_id: str` - which skill call produced this + - [ ] Field `timestamp: datetime` - when change was made + - [ ] Field `validation_result: ValidationResult | None` + - [ ] Define `Edit` model for targeted edits: + - [ ] Field `type: EditType` - search_replace, line_range, insert_after, etc. + - [ ] Field `search: str | None` - text to find (for search_replace) + - [ ] Field `replace: str | None` - replacement text + - [ ] Field `start_line: int | None` - for line-based edits + - [ ] Field `end_line: int | None` - for line-based edits + - [ ] Field `content: str | None` - content to insert + - [ ] Enhance `ChangeSet` model with: + - [ ] Field `changes: list[Change]` - all resource changes + - [ ] Field `warnings: list[str]` - non-blocking issues + - [ ] Field `skill_invocations: list[SkillInvocation]` - full invocation history + - [ ] Field `generated_by_actor: str` - actor that produced this + - [ ] Field `validation: ChangeSetValidation` - overall validation + - [ ] Method `add_change(change: Change)` - append change from skill + - [ ] Method `get_change(path: str) -> Change | None` - find by path + - [ ] Method `get_changes_by_skill(skill_id: str) -> list[Change]` - changes from specific skill + - [ ] Method `file_paths() -> list[str]` - all affected paths + - [ ] Method `to_diff() -> str` - unified diff of all changes + - [ ] Method `rollback_to(change_id: str)` - remove changes after point + - [ ] **C4.2** [Luis] Implement `SkillInvocationTracker` in `src/cleveragents/actor/skills/tracker.py`: + - [ ] **C4.2a** `SkillInvocation` model: + - [ ] Field `id: str` - unique invocation ID + - [ ] Field `skill_name: str` - which skill was called + - [ ] Field `parameters: dict` - input parameters + - [ ] Field `result: Any` - skill return value + - [ ] Field `changes: list[Change]` - resource changes produced + - [ ] Field `timestamp: datetime` - when invoked + - [ ] Field `duration_ms: int` - execution time + - [ ] Field `error: str | None` - if skill failed + - [ ] **C4.2b** `SkillInvocationTracker` class: + - [ ] Method `start_invocation(skill: Skill, params: dict) -> str` - begin tracking + - [ ] Method `record_change(invocation_id: str, change: Change)` - record change + - [ ] Method `complete_invocation(invocation_id: str, result: Any)` - finish tracking + - [ ] Method `fail_invocation(invocation_id: str, error: Exception)` - record failure + - [ ] Method `get_invocations() -> list[SkillInvocation]` - full history + - [ ] Method `build_changeset() -> ChangeSet` - assemble from invocations + - [ ] **C4.3** [Luis] Implement `ToolCallRouter` in `src/cleveragents/actor/skills/router.py`: + - [ ] **C4.3a** Parse LLM tool calls (NOT text output): + - [ ] Handle OpenAI-style tool_calls from response + - [ ] Handle Anthropic-style tool_use blocks + - [ ] Handle LangChain AgentAction format + - [ ] **C4.3b** Route tool calls to skills: + - [ ] Look up skill by name in registry + - [ ] Validate parameters against skill schema + - [ ] Check capability metadata (read_only, writes, etc.) + - [ ] Enforce permission restrictions + - [ ] **C4.3c** Execute with tracking: + - [ ] Start invocation tracking + - [ ] Execute skill in sandbox context + - [ ] Record changes produced by skill + - [ ] Complete invocation tracking + - [ ] Return result to LLM + - [ ] **C4.4** [Luis] Implement resource path validation in `src/cleveragents/actor/skills/path_validator.py`: + - [ ] Validate paths against sandbox boundaries + - [ ] Enforce deny-list patterns (.git/, node_modules/, __pycache__/, etc.) + - [ ] Auto-create parent directories for new file paths + - [ ] Resolve relative paths to absolute sandbox paths + - [ ] Detect path traversal attempts (../) + - [ ] **C4.5** [Luis] Create diff generation in `src/cleveragents/agents/diff_generator.py`: + - [ ] Method `generate_unified_diff(change: Change, sandbox: Sandbox) -> str`: + - [ ] Compare sandbox state with original + - [ ] Generate unified diff format + - [ ] Include file headers with paths + - [ ] Method `generate_changeset_diff(changeset: ChangeSet, sandbox: Sandbox) -> str`: + - [ ] Combine all change diffs + - [ ] Add summary header (files created, modified, deleted) + - [ ] Include statistics (lines added/removed) + - [ ] Method `generate_edit_preview(edit: Edit, original: str) -> str`: + - [ ] Show what an edit will change + - [ ] Highlight search/replace matches + - [ ] Tests: Tool-based change tracking tests + - [ ] **C4.6** [Rui] Write Behave scenarios in `features/change_tracking.feature`: + - [ ] Scenario: Skill invocation creates Change record + - [ ] Scenario: Multiple skill calls accumulate in ChangeSet + - [ ] Scenario: ChangeSet correctly tracks skill invocation history + - [ ] Scenario: Failed skill invocation is recorded with error + - [ ] Scenario: Generate unified diff from ChangeSet + - [ ] Scenario: Rollback to specific change point + - [ ] **C4.7** [Rui] Write Behave scenarios in `features/tool_call_routing.feature`: + - [ ] Scenario: Route OpenAI-style tool call to skill + - [ ] Scenario: Route Anthropic-style tool_use to skill + - [ ] Scenario: Validate parameters against skill schema + - [ ] Scenario: Reject tool call for read_only skill trying to write + - [ ] Scenario: Path validation rejects traversal attempt + - [ ] Scenario: Path validation auto-creates directories -**Parallel Group C2: Actor Loading & Compilation [Aditya + Jeff]** (depends on C1) - **PARALLEL SUBTRACK C2.legacy [Jeff]**: Remove v2 actor config compatibility (after C1.schema) - **SEQUENTIAL NOTE**: C2.legacy must land before C2.loader/C2.compiler to avoid dual-format support. -- [ ] **COMMIT (Owner: Aditya | Group: C2.loader) - Commit message: "feat(actor): add actor registry and loader"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Aditya]: Implement actor loader/registry with namespaced lookup, cache invalidation, and file discovery in `actors/` and `examples/actors/`. - - [ ] Code [Aditya]: Add registry integration with Tool Registry so tool nodes resolve at load time. - - [ ] Docs [Aditya]: Add `docs/reference/actors_loading.md` with discovery rules and namespaces. - - [ ] Tests (Behave) [Aditya]: Add `features/actor_loading.feature` for discovery, duplicates, and namespace lookup. - - [ ] Tests (Robot) [Aditya]: Add `robot/actor_loading.robot` for loader smoke tests. - - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/actor_loading_bench.py` for registry load performance. - - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Aditya]: `git commit -m "feat(actor): add actor registry and loader"`. -- [ ] **COMMIT (Owner: Jeff | Group: C2.compiler) - Commit message: "feat(actor): compile actor configs to LangGraph"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement ActorCompiler that builds LangGraph for LLM, TOOL, and GRAPH actors with tool node wiring. - - [ ] Code [Jeff]: Resolve tool node references through Tool Registry and validate required bindings before compile. - - [ ] Docs [Jeff]: Add `docs/reference/actors_compilation.md` covering compile outputs and error modes. - - [ ] Tests (Behave) [Jeff]: Add `features/actor_compilation.feature` for LLM/GRAPH compilation and tool node wiring. - - [ ] Tests (Robot) [Jeff]: Add `robot/actor_compilation.robot` smoke test compiling all examples. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/actor_compilation_bench.py` for compilation speed. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(actor): compile actor configs to LangGraph"`. -- [ ] **COMMIT (Owner: Jeff | Group: C2.refs) - Commit message: "feat(actor): resolve actor references and subgraphs"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement reference resolution, cycle detection, and subgraph wiring for actor refs. - - [ ] Code [Jeff]: Ensure cross-namespace reference resolution follows `[server:]namespace/name` rules. - - [ ] Docs [Jeff]: Update `docs/reference/actors_compilation.md` with reference semantics. - - [ ] Tests (Behave) [Jeff]: Add `features/actor_reference_resolution.feature` for missing/recursive refs. - - [ ] Tests (Robot) [Jeff]: Add `robot/actor_reference_resolution.robot` for subgraph wiring. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/actor_reference_bench.py` for reference resolution performance. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(actor): resolve actor references and subgraphs"`. +- [ ] **Stage C5: Validation Pipeline** (Day 9-11) **[Luis]** + - [ ] Code: Implement real validation gates + - [ ] **C5.1** [Luis] Create `ValidationPipeline` in `src/cleveragents/application/services/validation_service.py`: + - [ ] Method `validate_changeset(changeset: ChangeSet, project: Project) -> ValidationResult`: + - [ ] Run all applicable validators + - [ ] Aggregate results + - [ ] Return pass/fail with details + - [ ] Method `validate_syntax(change: Change) -> ValidationResult`: + - [ ] Detect language from extension + - [ ] Run language-specific syntax check + - [ ] Method `validate_lint(change: Change, config: ValidationConfig) -> ValidationResult`: + - [ ] Run lint command from project config + - [ ] Parse lint output for errors + - [ ] Method `validate_tests(project: Project, config: ValidationConfig) -> ValidationResult`: + - [ ] Run test command from project config + - [ ] Parse test results + - [ ] Method `validate_build(project: Project, config: ValidationConfig) -> ValidationResult`: + - [ ] Run build command from project config + - [ ] Check for build errors + - [ ] **C5.2** [Luis] Implement language-specific validators: + - [ ] Python: `python -m py_compile ` + - [ ] JavaScript/TypeScript: `node --check ` or syntax parse + - [ ] JSON: `json.loads()` validation + - [ ] YAML: `yaml.safe_load()` validation + - [ ] **C5.3** [Luis] Implement validation failure handling: + - [ ] If validation fails, attempt repair loop: + - [ ] Send errors to LLM with request to fix + - [ ] Parse fixed output + - [ ] Re-validate + - [ ] Max 3 repair attempts + - [ ] If repair fails, mark changeset as errored + - [ ] Preserve original output for debugging + - [ ] **C5.4** [Luis] Remove stub validation from existing code: + - [ ] Replace "output length > 10" check with real validation + - [ ] Remove "PASS" stub validation + - [ ] Tests: Validation tests + - [ ] **C5.5** [Rui] Write Behave scenarios in `features/validation_pipeline.feature`: + - [ ] Scenario: Valid Python file passes syntax validation + - [ ] Scenario: Invalid Python file fails with clear error + - [ ] Scenario: Lint errors detected and reported + - [ ] Scenario: Test failures detected and reported + - [ ] Scenario: Validation repair loop fixes simple errors + - [ ] Scenario: Validation repair gives up after max attempts -- [ ] **COMMIT (Owner: Jeff | Group: C2.legacy) - Commit message: "refactor(actor): drop v2 actor config compatibility"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Remove v2 JSON/YAML parsing paths in `src/cleveragents/actor/config.py` and related template engine usage. - - [ ] Code [Jeff]: Ensure only v3 actor YAML schema is accepted; provide clear error message when v2 fields are present. - - [ ] Docs [Jeff]: Update `docs/reference/actors_loading.md` with v3-only note and migration guidance. - - [ ] Tests (Behave) [Jeff]: Add scenarios that reject v2 actor config files. - - [ ] Tests (Robot) [Jeff]: Add Robot tests that attempt to load v2 configs and assert failure. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/actor_schema_reject_bench.py` for validation overhead. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "refactor(actor): drop v2 actor config compatibility"`. +- [ ] **Stage C6: Built-in Provider Actors** (Day 10-11) **[Aditya]** + - [ ] Code: Create built-in actors for each provider + - [ ] **C6.1** [Aditya] Generate built-in actor configs in `src/cleveragents/actor/builtins.py`: + - [ ] `openai/gpt-4` - GPT-4 wrapper + - [ ] `openai/gpt-4-turbo` - GPT-4 Turbo wrapper + - [ ] `openai/gpt-3.5-turbo` - GPT-3.5 wrapper + - [ ] `anthropic/claude-3-opus` - Claude 3 Opus wrapper + - [ ] `anthropic/claude-3-sonnet` - Claude 3 Sonnet wrapper + - [ ] `anthropic/claude-3-haiku` - Claude 3 Haiku wrapper + - [ ] `google/gemini-pro` - Gemini Pro wrapper + - [ ] `google/gemini-ultra` - Gemini Ultra wrapper + - [ ] **C6.2** [Aditya] Ensure built-in actors work as strategy/execution actors: + - [ ] Add appropriate system prompts for each role + - [ ] Configure temperature defaults (lower for execution) + - [ ] Test with plan lifecycle + - [ ] **C6.3** [Aditya] Implement provider capability detection: + - [ ] Check which API keys are configured + - [ ] Only register actors for available providers + - [ ] Clear error message for unavailable providers + - [ ] Tests: Verify built-in actors work in plan lifecycle + - [ ] **C6.4** [Rui] Write Behave scenarios in `features/builtin_actors.feature`: + - [ ] Scenario: Built-in OpenAI actor loads correctly + - [ ] Scenario: Built-in Anthropic actor loads correctly + - [ ] Scenario: Built-in actor can be used as strategy actor + - [ ] Scenario: Missing API key produces clear error -**Parallel Group C3: Skill Protocol & Context [Jeff]** (critical path; depends on C1) -- [ ] **COMMIT (Owner: Jeff | Group: C3.protocol) - Commit message: "feat(skill): add skill protocol and metadata"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Define Skill protocol interface, SkillMetadata, SkillResult, and SkillError types in `src/cleveragents/skills/protocol.py`. - - [ ] Code [Jeff]: Add `SkillDefinition` model that references Tool Registry names and optional inline tool definitions. - - [ ] Code [Jeff]: Add error mapping helpers to normalize tool failures into SkillError payloads. - - [ ] Docs [Jeff]: Add `docs/reference/skills_protocol.md` describing metadata, tool composition, and JSON schema rules. - - [ ] Tests (Behave) [Jeff]: Add `features/skill_protocol.feature` for metadata validation and error capture. - - [ ] Tests (Robot) [Jeff]: Add `robot/skill_protocol.robot` smoke tests. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/skill_protocol_bench.py` for validation throughput. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(skill): add skill protocol and metadata"`. -- [ ] **COMMIT (Owner: Jeff | Group: C3.context) - Commit message: "feat(skill): add skill context and registry"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement SkillContext (plan/resource access, sandbox path, change tracker) and SkillRegistry in `src/cleveragents/skills/context.py`. - - [ ] Code [Jeff]: Wire SkillRegistry to Tool Registry for tool resolution and validation node inclusion. - - [ ] Code [Jeff]: Add context helpers for resolving bound resources and exposing plan metadata. - - [ ] Docs [Jeff]: Add `docs/reference/skills_context.md` with context fields and helper methods. - - [ ] Tests (Behave) [Jeff]: Add `features/skill_context.feature` for sandboxed access and registry resolution. - - [ ] Tests (Robot) [Jeff]: Add `robot/skill_context.robot` for registry smoke tests. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/skill_context_bench.py` for registry resolution overhead. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(skill): add skill context and registry"`. -- [ ] **COMMIT (Owner: Jeff | Group: C3.inline) - Commit message: "feat(skill): add inline tool executor"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement inline tool execution with timeouts and restricted environment in `src/cleveragents/skills/inline_executor.py`. - - [ ] Code [Jeff]: Ensure inline tools conform to Tool Registry schema and return structured results. - - [ ] Code [Jeff]: Add safeguards for file/network access inside inline tools (local-only for MVP). - - [ ] Docs [Jeff]: Add `docs/reference/skills_inline.md` with safety constraints. - - [ ] Tests (Behave) [Jeff]: Add `features/skill_inline.feature` for execution and timeout handling. - - [ ] Tests (Robot) [Jeff]: Add `robot/skill_inline.robot` for inline tool smoke tests. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/inline_tool_bench.py` for execution overhead. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(skill): add inline tool executor"`. - -**Parallel Group C4: Built-in Skills [Jeff + Luis]** (depends on C3) -- [ ] **COMMIT (Owner: Jeff | Group: C4.file) - Commit message: "feat(skill): add file operation skills"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement ReadFile, WriteFile, EditFile, and DeleteFile tools with read_only enforcement. - - [ ] Code [Jeff]: Register tools in Tool Registry with resource bindings for fs/git resources and sandbox path rewrite. - - [ ] Code [Jeff]: Add content size limits and encoding normalization (UTF-8) for file tools. - - [ ] Docs [Jeff]: Add `docs/reference/skills_file.md` with examples and error cases. - - [ ] Tests (Behave) [Jeff]: Add `features/skill_file_ops.feature` for read/write/edit/delete flows. - - [ ] Tests (Robot) [Jeff]: Add `robot/skill_file_ops.robot` for file ops integration. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/file_tool_bench.py` for read/write throughput. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(skill): add file operation skills"`. -- [ ] **COMMIT (Owner: Jeff | Group: C4.search) - Commit message: "feat(skill): add directory and search skills"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement ListDir, Glob, and Grep tools with ignore patterns and size limits. - - [ ] Code [Jeff]: Register tools in Tool Registry with resource bindings and sandbox awareness. - - [ ] Code [Jeff]: Enforce include/exclude glob filters from project context policies. - - [ ] Docs [Jeff]: Add `docs/reference/skills_search.md` with examples. - - [ ] Tests (Behave) [Jeff]: Add `features/skill_search.feature` for listing/globbing/searching. - - [ ] Tests (Robot) [Jeff]: Add `robot/skill_search.robot` for search integration. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/search_tool_bench.py` for search performance. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(skill): add directory and search skills"`. -- [ ] **COMMIT (Owner: Luis | Group: C4.git) - Commit message: "feat(skill): add git operation skills"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Implement read-only git tools (status, diff, log, show) for sandboxed repos. - - [ ] Code [Luis]: Register git tools in Tool Registry with read-only capability metadata. - - [ ] Code [Luis]: Add path guards to ensure git tools only run inside sandbox root. - - [ ] Docs [Luis]: Add `docs/reference/skills_git.md` clarifying no destructive ops in MVP. - - [ ] Tests (Behave) [Luis]: Add `features/skill_git.feature` for git tool outputs. - - [ ] Tests (Robot) [Luis]: Add `robot/skill_git.robot` for git tool integration. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/git_tool_bench.py` for diff/log performance. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(skill): add git operation skills"`. - -**Parallel Group C5: Tool Routing & Change Tracking [Luis + Jeff]** (depends on C3/C4) -- [ ] **COMMIT (Owner: Luis | Group: C5.model) - Commit message: "feat(change): add ChangeSet models and invocation tracker"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Add Change/ChangeSet/ToolInvocation models and SkillInvocationTracker. - - [ ] Code [Luis]: Ensure ChangeSet stores resource references, sandbox paths, tool metadata, and timestamps. - - [ ] Code [Luis]: Add ChangeSet serialization helper for plan diff output (group by resource). - - [ ] Docs [Luis]: Add `docs/reference/change_tracking.md` describing tool-to-change mapping. - - [ ] Tests (Behave) [Luis]: Add `features/change_tracking.feature` for ChangeSet aggregation. - - [ ] Tests (Robot) [Luis]: Add `robot/change_tracking.robot` for tracker smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/change_tracking_bench.py` for invocation tracking overhead. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(change): add ChangeSet models and invocation tracker"`. -- [ ] **COMMIT (Owner: Jeff | Group: C5.router) - Commit message: "feat(change): add tool router for providers"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement ToolCallRouter for OpenAI/Anthropic/LangChain tool schemas with deterministic IDs. - - [ ] Code [Jeff]: Add mapping for tool/validation names and argument schemas based on Tool Registry metadata. - - [ ] Code [Jeff]: Add tool-call result normalization to match ToolInvocation schema. - - [ ] Docs [Jeff]: Add `docs/reference/tool_router.md` with provider-specific mappings. - - [ ] Tests (Behave) [Jeff]: Add `features/tool_router.feature` for schema mapping. - - [ ] Tests (Robot) [Jeff]: Add `robot/tool_router.robot` for routing smoke tests. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/tool_router_bench.py` for routing performance. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(change): add tool router for providers"`. -- [ ] **COMMIT (Owner: Luis | Group: C5.diff) - Commit message: "feat(change): add diff review artifacts"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Implement DiffBuilder and ReviewArtifact models for CLI review. - - [ ] Code [Luis]: Add support for multi-resource diffs and per-resource grouping. - - [ ] Code [Luis]: Add diff output serializers for rich/plain/json formats. - - [ ] Docs [Luis]: Add `docs/reference/diff_review.md` with output format. - - [ ] Tests (Behave) [Luis]: Add `features/diff_review.feature` for diff generation. - - [ ] Tests (Robot) [Luis]: Add `robot/diff_review.robot` for review artifacts. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/diff_review_bench.py` for diff building performance. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(change): add diff review artifacts"`. - -**Parallel Group C6: Validation Pipeline [Luis + Jeff]** (depends on C5 and validation attachment config) -- [ ] **COMMIT (Owner: Luis | Group: C6.pipeline) - Commit message: "feat(validation): add validation pipeline and results model"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Implement ValidationCommand, ValidationResult, and ValidationPipeline using Validation attachments from Tool Registry. - - [ ] Code [Luis]: Run validations at end of Execute phase only; do not re-run during Apply per spec. - - [ ] Code [Luis]: Enforce required vs informational validation modes and fix-then-revalidate loop hooks. - - [ ] Code [Luis]: Persist validation summary into Plan metadata for later review. - - [ ] Docs [Luis]: Add `docs/reference/validation_pipeline.md` with ordering, timeouts, and failure handling. - - [ ] Tests (Behave) [Luis]: Add `features/validation_pipeline.feature` for pass/fail paths and required/informational modes. - - [ ] Tests (Robot) [Luis]: Add `robot/validation_pipeline.robot` for pipeline smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/validation_pipeline_bench.py` for pipeline runtime. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(validation): add validation pipeline and results model"`. -- [ ] **COMMIT (Owner: Jeff | Group: C6.wraps) - Commit message: "feat(validation): support wrapped tools and transforms"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement validation `wraps` execution path that runs the wrapped Tool and captures its output. - - [ ] Code [Jeff]: Add transform engine that maps wrapped tool output into ValidationResult schema (must output `passed` boolean). - - [ ] Code [Jeff]: Enforce read-only constraints for validations even when wrapping write-capable tools; block if violation. - - [ ] Docs [Jeff]: Update `docs/reference/validation_model.md` with `wraps` + `transform` examples and safety rules. - - [ ] Tests (Behave) [Jeff]: Add wrapped-validation scenarios with transform success/fail paths. - - [ ] Tests (Robot) [Jeff]: Add `robot/validation_wraps.robot` for wrapped validation end-to-end. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/validation_wraps_bench.py` for transform overhead. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(validation): support wrapped tools and transforms"`. -- [ ] **COMMIT (Owner: Jeff | Group: C6.gating) - Commit message: "feat(validation): integrate validation with apply gating"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Block apply on required validation failure; surface validation artifacts for review. - - [ ] Code [Jeff]: Ensure informational validation failures do not block apply but are logged in plan status. - - [ ] Code [Jeff]: Add CLI status output for validation summary (required vs informational counts). - - [ ] Docs [Jeff]: Update `docs/reference/plan_actor_integration.md` with validation gating behavior. - - [ ] Tests (Behave) [Jeff]: Add `features/validation_gating.feature` for apply blocking. - - [ ] Tests (Robot) [Jeff]: Add `robot/validation_gating.robot` for end-to-end gating. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/validation_gating_bench.py` for gating overhead. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(validation): integrate validation with apply gating"`. - -**Parallel Group C7: MCP Adapter [Aditya]** (depends on C3) -- [ ] **COMMIT (Owner: Aditya | Group: C7.mcp) - Commit message: "feat(skill): add MCP adapter for external tools"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Aditya]: Implement MCP client adapter conforming to Tool interface with connection config. - - [ ] Code [Aditya]: Register MCP tools in Tool Registry with dynamic discovery from MCP server. - - [ ] Code [Aditya]: Add timeout and retry defaults for MCP calls (local-only for MVP). - - [ ] Docs [Aditya]: Add `docs/reference/skills_mcp.md` with server connection examples. - - [ ] Tests (Behave) [Aditya]: Add `features/skill_mcp.feature` for MCP tool calls. - - [ ] Tests (Robot) [Aditya]: Add `robot/skill_mcp.robot` for MCP adapter smoke test. - - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/mcp_adapter_bench.py` for tool invocation latency. - - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Aditya]: `git commit -m "feat(skill): add MCP adapter for external tools"`. - -**Parallel Group C8: Built-in Provider Actors [Aditya]** (depends on C1/C2) -- [ ] **COMMIT (Owner: Aditya | Group: C8.providers) - Commit message: "feat(actor): add built-in provider actors"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Aditya]: Add built-in actor configs for `openai/`, `anthropic/`, and `openrouter/` (plus `google/` if configured). - - [ ] Code [Aditya]: Add built-in actors for invariant reconciliation and estimation roles (using provider defaults). - - [ ] Docs [Aditya]: Add `docs/reference/provider_actors.md` with provider defaults. - - [ ] Tests (Behave) [Aditya]: Add `features/provider_actors.feature` for built-in actor loading. - - [ ] Tests (Robot) [Aditya]: Add `robot/provider_actors.robot` for registry visibility. - - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/provider_actor_load_bench.py` for registry load cost. - - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Aditya]: `git commit -m "feat(actor): add built-in provider actors"`. - -**Parallel Group C9: Plan-Actor Integration [Jeff + Luis]** (depends on C2/C5/C6) -- [ ] **COMMIT (Owner: Jeff | Group: C9.execute) - Commit message: "feat(plan): execute strategize and execute phases via actors"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Connect PlanLifecycleService to actor execution for Strategize and Execute phases. - - [ ] Code [Jeff]: Ensure Strategize is read-only and records decisions without modifying resources. - - [ ] Code [Jeff]: Ensure Execute uses sandbox resources and tool calls routed through Tool Router + ChangeSet. - - [ ] Code [Jeff]: Add plan status updates for phase start/complete/fail during actor execution. - - [ ] Docs [Jeff]: Add `docs/reference/plan_actor_integration.md` with phase flow. - - [ ] Tests (Behave) [Jeff]: Add `features/plan_actor_integration.feature` for strategy/execute flows. - - [ ] Tests (Robot) [Jeff]: Add `robot/plan_actor_integration.robot` for end-to-end actor execution. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/plan_actor_integration_bench.py` for execution overhead. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(plan): execute strategize and execute phases via actors"`. -- [ ] **COMMIT (Owner: Jeff | Group: C9.apply) - Commit message: "feat(plan): integrate change review and apply flow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Wire ChangeSet review artifacts into `plan diff` and review-before-apply flow. - - [ ] Code [Jeff]: Ensure Apply merges sandbox into real resources only after required validations pass. - - [ ] Code [Jeff]: Persist apply summary (files changed, validations) back into Plan metadata for `plan status`. - - [ ] Docs [Jeff]: Update CLI docs for `plan diff` and `plan apply` review output. - - [ ] Tests (Behave) [Jeff]: Add `features/plan_review_apply.feature` for review gate behavior. - - [ ] Tests (Robot) [Jeff]: Add `robot/plan_review_apply.robot` for review-before-apply path. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/plan_apply_bench.py` for apply throughput. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(plan): integrate change review and apply flow"`. +- [ ] **Stage C7: Plan-Actor Integration** (Day 11-14) **[Aditya + Luis]** + - [ ] Code: Connect actors to plan lifecycle + - [ ] **C7.1** [Aditya] Update `PlanLifecycleService.execute_strategize()`: + - [ ] Load strategy_actor from action + - [ ] Compile actor to LangGraph + - [ ] Build strategy context: + - [ ] Project resources + - [ ] Plan description and arguments + - [ ] Definition of done + - [ ] Invoke actor graph with context + - [ ] Parse strategy output + - [ ] Store strategy in plan + - [ ] **C7.2** [Aditya] Implement dependency closure computation: + - [ ] Method `compute_closure(target: str, project: Project) -> ResourceClosure`: + - [ ] Find direct imports/includes + - [ ] Find symbol dependencies + - [ ] Find test dependencies + - [ ] Find build references + - [ ] Integrate with strategy actor context + - [ ] **C7.3** [Luis] Update `PlanLifecycleService.execute_execution()`: + - [ ] Load execution_actor from action + - [ ] Compile actor to LangGraph + - [ ] Build execution context: + - [ ] Strategy output + - [ ] Resource service for sandbox access + - [ ] Bounded dependency closure + - [ ] Invoke actor graph with context + - [ ] Parse output as ChangeSet + - [ ] Run validation pipeline + - [ ] Handle subplan spawning + - [ ] **C7.4** [Luis] Update `PlanLifecycleService.apply_plan()`: + - [ ] Verify execution completed successfully + - [ ] Commit all sandboxes + - [ ] Apply ChangeSet to resources + - [ ] Record applied artifacts + - [ ] Clean up sandboxes + - [ ] **C7.4a** [Luis] Implement ATOMIC apply: + - [ ] Apply must be all-or-nothing: + - [ ] Write all changes to temp files first + - [ ] Validate all writes succeeded + - [ ] Atomic rename/swap to final locations + - [ ] If any step fails, rollback all changes + - [ ] In git mode: + - [ ] All changes in single commit + - [ ] If commit fails, no partial changes applied + - [ ] Handle partial failure: + - [ ] Preserve sandbox for inspection + - [ ] Clear error message about what failed + - [ ] Allow retry after manual fix + - [ ] **C7.5** [Aditya] Implement hierarchical task decomposition: + - [ ] Strategy actor can emit subplan decisions + - [ ] Each subplan gets bounded context + - [ ] Parallel or sequential execution modes + - [ ] **C7.6** [Luis] Connect output parser to execution flow: + - [ ] After actor produces output, parse to ChangeSet + - [ ] Validate ChangeSet + - [ ] Store ChangeSet in plan + - [ ] **C7.7** [Luis] Implement diff review artifact storage: + - [ ] Store generated diff in plan metadata + - [ ] Create `DiffArtifact` model: + - [ ] Field `diff_id: str` - ULID + - [ ] Field `plan_id: str` - parent plan + - [ ] Field `unified_diff: str` - full unified diff + - [ ] Field `file_summaries: list[FileSummary]` - per-file summary + - [ ] Field `risk_markers: list[str]` - touched auth code, migrations, etc. + - [ ] Field `created_at: datetime` + - [ ] Display diff in `agents [--data-dir PATH] [--config-path PATH] plan diff` command + - [ ] Display diff before apply in review-before-apply mode + - [ ] Tests: End-to-end tests for full plan lifecycle with actors + - [ ] **C7.7** [Rui] Write Behave scenarios in `features/plan_actor_integration.feature`: + - [ ] Scenario: Full lifecycle with LLM actors (mocked) + - [ ] Scenario: Strategy actor receives correct context + - [ ] Scenario: Execution actor receives strategy output + - [ ] Scenario: ChangeSet applied correctly + - [ ] Scenario: Validation failure triggers repair loop + - [ ] **C7.8** [Rui] Write Robot integration test `robot/plan_actor_integration.robot`: + - [ ] Test: Full lifecycle with real sandbox + - [ ] Test: Multi-file generation and application + - [ ] Tests: Dependency closure computation accuracy + - [ ] **C7.9** [Rui] Write Behave scenarios in `features/dependency_closure.feature`: + - [ ] Scenario: Python imports detected correctly + - [ ] Scenario: Test file dependencies included **M3 SUCCESS CRITERIA**: -- Actor YAML schema validated; examples load and compile to LangGraph. -- Skills execute via SkillContext; built-in file/dir/search/git skills available. -- Tool-based change tracking (no output parsing) produces ChangeSet and diff review artifacts. -- MCP adapter executes a tool against a test MCP server. -- Built-in provider actors available (`openai/`, `anthropic/`, `openrouter/` as configured). -- Validation pipeline runs validation attachments and blocks apply on required failure. -- Plan lifecycle uses actors for Strategize/Execute and applies ChangeSet after review. -- `nox` passes with coverage >=97% across actor/skill/change-tracking suites. +- [ ] Can define actors in YAML with skills +- [ ] Actors compile to LangGraph graphs +- [ ] Skills can execute inline Python code +- [ ] Built-in resource skills work (read/write/edit/delete files) +- [ ] MCP skill adapter connects to external servers +- [ ] Built-in provider actors work +- [ ] Tool-based change tracking builds ChangeSet from skill invocations +- [ ] Validation pipeline catches errors +- [ ] Full plan lifecycle works: Action -> Strategize (with actor) -> Execute (with actor) -> Apply **--- MERGE POINT 1: After M3, all workstreams coordinate ---** @@ -2291,411 +4832,3485 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target ### Section 6: Execution Pipeline, Decisions & Invariants [M3-M4] **Target: Milestone M4 (+21 days)** -**Week 3 focus**: decision capture, correction, invariants, and DoD gating. -**Parallel Group D1: Decision Domain [Hamza + Rui]** (foundation for D2-D5) -- [ ] **COMMIT (Owner: Hamza | Group: D1.domain) - Commit message: "feat(domain): add decision model and context snapshots"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Add `DecisionType`, `ContextSnapshot`, and `Decision` models with correction fields and helpers. - - [ ] Code [Hamza]: Include required fields: question, chosen option, alternatives, confidence score, rationale, dependencies, and context hash. - - [ ] Docs [Hamza]: Add `docs/reference/decision_model.md` with examples and schema notes. - - [ ] Tests (Behave) [Hamza]: Add `features/decision_model.feature` for validation and helpers. - - [ ] Tests (Robot) [Hamza]: Add `robot/decision_model.robot` smoke tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/decision_model_bench.py` for decision validation throughput. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(domain): add decision model and context snapshots"`. +- [ ] **Stage D1: Decision Data Model** (Day 15-16) **[Hamza - Well Rounded]** + + **SEQUENTIAL ORDER**: D1.1 (Enums) → D1.2 (ContextSnapshot) → D1.3 (Decision model) → D1.4 (Helpers) → D1.5 (Tests) + + - [ ] Code: Create Decision domain model + - [ ] **D1.1** [Hamza] Define `DecisionType` enum in `src/cleveragents/domain/models/core/decision.py`: + - [ ] **D1.1a** [Hamza] Create file with DecisionType enum: + ```python + from enum import Enum + + class DecisionType(str, Enum): + """Classification of decision points in plan execution.""" + + # Root decisions + PROMPT_DEFINITION = "prompt_definition" # Initial plan prompt + + # Strategy phase decisions + STRATEGY_CHOICE = "strategy_choice" # High-level approach + IMPLEMENTATION_CHOICE = "implementation_choice" # How to implement + RESOURCE_SELECTION = "resource_selection" # Which resources to use + + # Execution phase decisions + SUBPLAN_SPAWN = "subplan_spawn" # Decision to create subplan + TOOL_INVOCATION = "tool_invocation" # Which tool/skill to use + + # Error handling decisions + ERROR_RECOVERY = "error_recovery" # How to handle failure + VALIDATION_RESPONSE = "validation_response" # Response to validation failure + + # User interaction decisions + USER_INTERVENTION = "user_intervention" # User provided guidance + ``` + - [ ] Commit: "feat(domain): define DecisionType enum" + - [ ] **D1.1b** [Hamza] Add helper method for decision classification: + ```python + @classmethod + def is_strategy_decision(cls, decision_type: "DecisionType") -> bool: + """Check if this is a strategy phase decision.""" + return decision_type in { + cls.PROMPT_DEFINITION, cls.STRATEGY_CHOICE, + cls.IMPLEMENTATION_CHOICE, cls.RESOURCE_SELECTION + } + + @classmethod + def is_execution_decision(cls, decision_type: "DecisionType") -> bool: + """Check if this is an execution phase decision.""" + return decision_type in {cls.SUBPLAN_SPAWN, cls.TOOL_INVOCATION} + ``` + - [ ] Commit: "feat(domain): add DecisionType helper methods" + - [ ] **D1.2** [Hamza] Define `ContextSnapshot` model: + - [ ] **D1.2a** [Hamza] Create ContextSnapshot dataclass: + ```python + @dataclass(frozen=True) + class ContextSnapshot: + """Snapshot of context at decision point for replay.""" + + snapshot_id: str # ULID + hot_context_hash: str # SHA-256 hash of hot context content + hot_context_ref: str # Storage reference (file path or blob ID) + relevant_resources: tuple[str, ...] # Resource IDs in scope + actor_state_ref: str | None # LangGraph checkpoint ID + file_versions: dict[str, str] # path -> git commit or hash + created_at: datetime + ``` + - [ ] Commit: "feat(domain): define ContextSnapshot dataclass" + - [ ] **D1.2b** [Hamza] Add factory method: + ```python + @classmethod + def capture( + cls, + hot_context: str, + resources: list[str], + actor_state: str | None = None, + file_versions: dict[str, str] | None = None + ) -> "ContextSnapshot": + """Capture a snapshot of current context.""" + import hashlib + import ulid + + return cls( + snapshot_id=ulid.new().str, + hot_context_hash=hashlib.sha256(hot_context.encode()).hexdigest(), + hot_context_ref="", # Set by storage layer + relevant_resources=tuple(resources), + actor_state_ref=actor_state, + file_versions=file_versions or {}, + created_at=datetime.utcnow() + ) + ``` + - [ ] Commit: "feat(domain): add ContextSnapshot.capture() factory" + - [ ] **D1.3** [Hamza] Define `Decision` Pydantic model: + - [ ] **D1.3a** [Hamza] Create Decision class with identity fields: + ```python + class Decision(BaseModel): + """A recorded decision point in plan execution.""" + + model_config = ConfigDict(frozen=True) + + # Identity + decision_id: str = Field(..., description="ULID identifier") + plan_id: str = Field(..., description="Parent plan ULID") + + # Tree structure + parent_decision_id: str | None = Field( + default=None, description="Parent in decision tree" + ) + sequence_number: int = Field( + ..., ge=0, description="Order within plan (0=root)" + ) + ``` + - [ ] Commit: "feat(domain): add Decision model identity fields" + - [ ] **D1.3b** [Hamza] Add decision content fields: + ```python + # Decision content + decision_type: DecisionType = Field(..., description="Classification") + question: str = Field(..., min_length=1, description="What was decided") + chosen_option: str = Field(..., min_length=1, description="The choice made") + alternatives_considered: list[str] = Field( + default_factory=list, description="Other options evaluated" + ) + confidence_score: float | None = Field( + default=None, ge=0.0, le=1.0, description="AI confidence 0.0-1.0" + ) + rationale: str = Field(default="", description="Why this choice") + actor_reasoning: str | None = Field( + default=None, description="Raw LLM chain-of-thought" + ) + ``` + - [ ] Commit: "feat(domain): add Decision content fields" + - [ ] **D1.3c** [Hamza] Add context and relationship fields: + ```python + # Context for replay + context_snapshot: ContextSnapshot = Field( + ..., description="Snapshot at decision time" + ) + checkpoint_id: str | None = Field( + default=None, description="Sandbox checkpoint for rollback" + ) + + # Downstream relationships (populated during execution) + downstream_decision_ids: list[str] = Field( + default_factory=list, description="Decisions that depend on this" + ) + downstream_plan_ids: list[str] = Field( + default_factory=list, description="Subplans spawned from this" + ) + artifacts_produced: list[str] = Field( + default_factory=list, description="Artifact IDs created" + ) + ``` + - [ ] Commit: "feat(domain): add Decision context and relationship fields" + - [ ] **D1.3d** [Hamza] Add correction tracking fields: + ```python + # Correction tracking + is_correction: bool = Field( + default=False, description="Is this a corrected decision" + ) + corrects_decision_id: str | None = Field( + default=None, description="Original decision this corrects" + ) + superseded_by: str | None = Field( + default=None, description="Decision that replaced this one" + ) + + # Timestamps + created_at: datetime = Field(default_factory=datetime.utcnow) + ``` + - [ ] Commit: "feat(domain): add Decision correction tracking fields" + - [ ] **D1.3e** [Hamza] Add validators: + ```python + @field_validator('decision_id', 'plan_id') + @classmethod + def validate_ulid(cls, v: str) -> str: + """Validate ULID format.""" + if len(v) != 26 or not v.isalnum(): + raise ValueError(f"Invalid ULID format: {v}") + return v + + @model_validator(mode='after') + def validate_correction_consistency(self) -> Self: + """Ensure correction fields are consistent.""" + if self.is_correction and not self.corrects_decision_id: + raise ValueError("Correction must specify corrects_decision_id") + if self.corrects_decision_id and not self.is_correction: + raise ValueError("corrects_decision_id requires is_correction=True") + return self + ``` + - [ ] Commit: "feat(domain): add Decision validators" + - [ ] **D1.4** [Hamza] Add Decision helper methods: + - [ ] **D1.4a** [Hamza] Add computed properties: + ```python + @property + def is_root(self) -> bool: + """Check if this is the root decision (no parent).""" + return self.parent_decision_id is None + + @property + def is_superseded(self) -> bool: + """Check if this decision has been replaced.""" + return self.superseded_by is not None + + @property + def has_downstream_work(self) -> bool: + """Check if this decision spawned work.""" + return bool(self.downstream_decision_ids or self.downstream_plan_ids) + + @property + def summary(self) -> str: + """Short summary for display.""" + q = self.question[:50] + "..." if len(self.question) > 50 else self.question + return f"[{self.decision_type.value}] {q}" + ``` + - [ ] Commit: "feat(domain): add Decision computed properties" + - [ ] **D1.4b** [Hamza] Add mutation methods (return new instance): + ```python + def with_downstream_decision(self, decision_id: str) -> "Decision": + """Return new Decision with added downstream decision.""" + return self.model_copy(update={ + "downstream_decision_ids": [*self.downstream_decision_ids, decision_id] + }) + + def with_downstream_plan(self, plan_id: str) -> "Decision": + """Return new Decision with added downstream plan.""" + return self.model_copy(update={ + "downstream_plan_ids": [*self.downstream_plan_ids, plan_id] + }) + + def with_artifact(self, artifact_id: str) -> "Decision": + """Return new Decision with added artifact.""" + return self.model_copy(update={ + "artifacts_produced": [*self.artifacts_produced, artifact_id] + }) + + def mark_superseded(self, by_decision_id: str) -> "Decision": + """Return new Decision marked as superseded.""" + return self.model_copy(update={"superseded_by": by_decision_id}) + ``` + - [ ] Commit: "feat(domain): add Decision mutation methods" + - [ ] Tests: Behave scenarios for decision model + - [ ] **D1.5** [Rui] Write Behave scenarios in `features/decision_model.feature`: + - [ ] **D1.5a** [Rui] Creation scenarios: + - [ ] Scenario: Create decision with all required fields + - [ ] Given valid decision_id, plan_id, question, chosen_option, context_snapshot + - [ ] When I create a Decision with these fields + - [ ] Then the Decision is created successfully + - [ ] And sequence_number defaults to provided value + - [ ] Scenario: Create root decision (no parent) + - [ ] When I create a Decision with parent_decision_id=None + - [ ] Then is_root property returns True + - [ ] Scenario: Create child decision + - [ ] When I create a Decision with parent_decision_id set + - [ ] Then is_root property returns False + - [ ] Commit: "test(behave): add decision creation scenarios" + - [ ] **D1.5b** [Rui] Validation scenarios: + - [ ] Scenario: Invalid ULID format rejected + - [ ] When I create a Decision with decision_id="invalid" + - [ ] Then validation error is raised + - [ ] Scenario: Confidence score must be 0.0-1.0 + - [ ] When I create a Decision with confidence_score=1.5 + - [ ] Then validation error is raised + - [ ] Scenario: Correction without corrects_decision_id fails + - [ ] When I create a Decision with is_correction=True and corrects_decision_id=None + - [ ] Then validation error mentions correction consistency + - [ ] Commit: "test(behave): add decision validation scenarios" + - [ ] **D1.5c** [Rui] DecisionType scenarios: + - [ ] Scenario: Each decision type validates correctly + - [ ] For each DecisionType enum value + - [ ] When I create a Decision with that type + - [ ] Then decision is created successfully + - [ ] Scenario: is_strategy_decision helper works + - [ ] Given DecisionType.STRATEGY_CHOICE + - [ ] Then DecisionType.is_strategy_decision() returns True + - [ ] Given DecisionType.TOOL_INVOCATION + - [ ] Then DecisionType.is_strategy_decision() returns False + - [ ] Commit: "test(behave): add DecisionType scenarios" + - [ ] **D1.5d** [Rui] Context snapshot scenarios: + - [ ] Scenario: ContextSnapshot.capture() creates valid snapshot + - [ ] Given hot_context string and resource list + - [ ] When I call ContextSnapshot.capture() + - [ ] Then snapshot has valid ULID + - [ ] And hot_context_hash is SHA-256 of content + - [ ] Scenario: ContextSnapshot is immutable + - [ ] Given a ContextSnapshot instance + - [ ] When I try to modify a field + - [ ] Then FrozenInstanceError is raised + - [ ] Commit: "test(behave): add ContextSnapshot scenarios" -**Parallel Group D2: Decision Recording Service [Hamza]** (depends on D1) -- [ ] **COMMIT (Owner: Hamza | Group: D2.service) - Commit message: "feat(service): add decision recording and snapshot store"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement `DecisionService` with `record_decision`, sequence numbers, tree queries, and downstream linking. - - [ ] Code [Hamza]: Add `ContextSnapshotStore` interface with a file-backed MVP implementation and hash dedupe. - - [ ] Code [Hamza]: Integrate decision recording into strategize/execute phases (prompt/strategy/subplan/tool decisions). - - [ ] Docs [Hamza]: Add `docs/reference/decision_service.md` covering recording and snapshots. - - [ ] Tests (Behave) [Hamza]: Add `features/decision_recording.feature` scenarios. - - [ ] Tests (Robot) [Hamza]: Add `robot/decision_recording.robot` integration smoke tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/decision_recording_bench.py` for record throughput. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(service): add decision recording and snapshot store"`. +- [ ] **Stage D2: Decision Recording** (Day 16-18) **[Hamza]** + + **SEQUENTIAL ORDER**: D2.1 (Service scaffold) → D2.2 (record_decision) → D2.3 (tree queries) → D2.4 (context capture) → D2.5 (strategy integration) → D2.6 (downstream updates) → D2.7 (Tests) + + - [ ] Code: Record decisions during Strategize + - [ ] **D2.1** [Hamza] Create `DecisionService` scaffold in `src/cleveragents/application/services/decision_service.py`: + - [ ] **D2.1a** [Hamza] Define service class with dependencies: + ```python + class DecisionService: + """Service for recording and querying decisions.""" + + def __init__( + self, + decision_repo: DecisionRepository, + snapshot_store: ContextSnapshotStore, + plan_repo: LifecyclePlanRepository + ): + self._decision_repo = decision_repo + self._snapshot_store = snapshot_store + self._plan_repo = plan_repo + self._sequence_counters: dict[str, int] = {} # plan_id -> next sequence + ``` + - [ ] Commit: "feat(service): add DecisionService scaffold" + - [ ] **D2.1b** [Hamza] Define ContextSnapshotStore protocol: + ```python + class ContextSnapshotStore(Protocol): + """Protocol for storing context snapshots.""" + + def store(self, snapshot: ContextSnapshot, content: str) -> ContextSnapshot: + """Store snapshot content and return with ref set.""" + ... + + def retrieve(self, snapshot_id: str) -> tuple[ContextSnapshot, str]: + """Retrieve snapshot and its content.""" + ... + + def retrieve_by_hash(self, hash: str) -> tuple[ContextSnapshot, str] | None: + """Retrieve by content hash (for deduplication).""" + ... + ``` + - [ ] Commit: "feat(service): define ContextSnapshotStore protocol" + - [ ] **D2.2** [Hamza] Implement `record_decision()` method: + - [ ] **D2.2a** [Hamza] Core implementation: + ```python + def record_decision( + self, + plan_id: str, + decision_type: DecisionType, + question: str, + chosen_option: str, + hot_context: str, + resources: list[str], + parent_decision_id: str | None = None, + alternatives: list[str] | None = None, + confidence: float | None = None, + rationale: str = "", + actor_reasoning: str | None = None, + checkpoint_id: str | None = None + ) -> Decision: + """Record a new decision for a plan.""" + import ulid + + # Generate IDs + decision_id = ulid.new().str + + # Get next sequence number for this plan + seq = self._get_next_sequence(plan_id) + + # Capture context snapshot + snapshot = self._capture_snapshot(hot_context, resources, checkpoint_id) + + # Create decision + decision = Decision( + decision_id=decision_id, + plan_id=plan_id, + parent_decision_id=parent_decision_id, + sequence_number=seq, + decision_type=decision_type, + question=question, + chosen_option=chosen_option, + alternatives_considered=alternatives or [], + confidence_score=confidence, + rationale=rationale, + actor_reasoning=actor_reasoning, + context_snapshot=snapshot, + checkpoint_id=checkpoint_id + ) + + # Persist + self._decision_repo.create(decision) + + # Update parent's downstream if applicable + if parent_decision_id: + self._add_downstream_decision(parent_decision_id, decision_id) + + logger.info(f"Recorded decision {decision_id}: {decision.summary}") + return decision + ``` + - [ ] Commit: "feat(service): implement record_decision()" + - [ ] **D2.2b** [Hamza] Add sequence number management: + ```python + def _get_next_sequence(self, plan_id: str) -> int: + """Get next sequence number for a plan.""" + if plan_id not in self._sequence_counters: + # Load max sequence from existing decisions + existing = self._decision_repo.get_max_sequence(plan_id) + self._sequence_counters[plan_id] = (existing or -1) + 1 + + seq = self._sequence_counters[plan_id] + self._sequence_counters[plan_id] += 1 + return seq + ``` + - [ ] Commit: "feat(service): add sequence number management" + - [ ] **D2.3** [Hamza] Implement tree query methods: + - [ ] **D2.3a** [Hamza] Implement `get_decision_tree()`: + ```python + def get_decision_tree(self, plan_id: str) -> list[Decision]: + """Get all decisions for a plan in tree order.""" + decisions = self._decision_repo.get_by_plan(plan_id) + + # Sort by sequence number to get chronological order + return sorted(decisions, key=lambda d: d.sequence_number) + + def get_decision_tree_nested(self, plan_id: str) -> DecisionTree: + """Get decisions as nested tree structure.""" + decisions = self.get_decision_tree(plan_id) + return self._build_tree(decisions) + + def _build_tree(self, decisions: list[Decision]) -> DecisionTree: + """Build tree from flat list of decisions.""" + by_id = {d.decision_id: d for d in decisions} + roots = [] + + for d in decisions: + if d.parent_decision_id is None: + roots.append(DecisionNode(decision=d, children=[])) + else: + # Find parent and add as child + # Implementation details... + + return DecisionTree(roots=roots, total_count=len(decisions)) + ``` + - [ ] Commit: "feat(service): implement decision tree queries" + - [ ] **D2.3b** [Hamza] Implement `get_decision()` and `get_children()`: + ```python + def get_decision(self, decision_id: str) -> Decision | None: + """Get a single decision by ID.""" + return self._decision_repo.get_by_id(decision_id) + + def get_children(self, decision_id: str) -> list[Decision]: + """Get all direct children of a decision.""" + return self._decision_repo.get_children(decision_id) + + def get_ancestors(self, decision_id: str) -> list[Decision]: + """Get all ancestors from decision to root.""" + ancestors = [] + current = self.get_decision(decision_id) + + while current and current.parent_decision_id: + parent = self.get_decision(current.parent_decision_id) + if parent: + ancestors.append(parent) + current = parent + + return ancestors + ``` + - [ ] Commit: "feat(service): implement get_decision and get_children" + - [ ] **D2.4** [Hamza] Implement context snapshot capture: + - [ ] **D2.4a** [Hamza] Implement `_capture_snapshot()`: + ```python + def _capture_snapshot( + self, + hot_context: str, + resources: list[str], + checkpoint_id: str | None = None + ) -> ContextSnapshot: + """Capture and store a context snapshot.""" + # Create snapshot object + snapshot = ContextSnapshot.capture( + hot_context=hot_context, + resources=resources, + actor_state=checkpoint_id + ) + + # Check for duplicate by hash (deduplication) + existing = self._snapshot_store.retrieve_by_hash(snapshot.hot_context_hash) + if existing: + logger.debug(f"Reusing existing snapshot with hash {snapshot.hot_context_hash[:8]}") + return existing[0] + + # Store new snapshot + stored = self._snapshot_store.store(snapshot, hot_context) + return stored + ``` + - [ ] Commit: "feat(service): implement context snapshot capture" + - [ ] **D2.4b** [Hamza] Implement FileContextSnapshotStore: + ```python + class FileContextSnapshotStore: + """Store snapshots in filesystem.""" + + def __init__(self, base_dir: Path): + self._base_dir = base_dir + self._base_dir.mkdir(parents=True, exist_ok=True) + + def store(self, snapshot: ContextSnapshot, content: str) -> ContextSnapshot: + """Store snapshot content to file.""" + file_path = self._base_dir / f"{snapshot.snapshot_id}.json" + + data = { + "snapshot": snapshot.__dict__, + "content": content + } + file_path.write_text(json.dumps(data)) + + # Update snapshot with ref + return dataclasses.replace( + snapshot, + hot_context_ref=str(file_path) + ) + ``` + - [ ] Commit: "feat(service): implement FileContextSnapshotStore" + - [ ] **D2.5** [Hamza] Integrate decision recording into strategy actor: + - [ ] **D2.5a** [Hamza] Create DecisionRecordingCallback: + ```python + class DecisionRecordingCallback: + """LangGraph callback to record decisions during execution.""" + + def __init__(self, decision_service: DecisionService, plan_id: str): + self._service = decision_service + self._plan_id = plan_id + self._current_parent: str | None = None + + def on_strategy_decision( + self, + question: str, + chosen: str, + alternatives: list[str], + confidence: float | None, + rationale: str, + context: str + ) -> Decision: + """Called when strategy actor makes a decision.""" + decision = self._service.record_decision( + plan_id=self._plan_id, + decision_type=DecisionType.STRATEGY_CHOICE, + question=question, + chosen_option=chosen, + hot_context=context, + resources=[], # Populated from plan + parent_decision_id=self._current_parent, + alternatives=alternatives, + confidence=confidence, + rationale=rationale + ) + return decision + ``` + - [ ] Commit: "feat(service): add DecisionRecordingCallback" + - [ ] **D2.5b** [Hamza] Record root PROMPT_DEFINITION decision: + ```python + def record_prompt_definition( + self, + plan_id: str, + prompt: str, + context: str + ) -> Decision: + """Record the initial prompt as root decision.""" + return self.record_decision( + plan_id=plan_id, + decision_type=DecisionType.PROMPT_DEFINITION, + question="What should be done?", + chosen_option=prompt, + hot_context=context, + resources=[], + parent_decision_id=None, + rationale="User provided prompt" + ) + ``` + - [ ] Commit: "feat(service): add record_prompt_definition()" + - [ ] **D2.6** [Hamza] Implement downstream relationship updates: + - [ ] **D2.6a** [Hamza] Add methods to update downstream fields: + ```python + def _add_downstream_decision(self, parent_id: str, child_id: str) -> None: + """Add child to parent's downstream_decision_ids.""" + parent = self._decision_repo.get_by_id(parent_id) + if parent: + updated = parent.with_downstream_decision(child_id) + self._decision_repo.update(updated) + + def add_downstream_plan(self, decision_id: str, plan_id: str) -> None: + """Record that a decision spawned a subplan.""" + decision = self._decision_repo.get_by_id(decision_id) + if decision: + updated = decision.with_downstream_plan(plan_id) + self._decision_repo.update(updated) + logger.info(f"Linked subplan {plan_id} to decision {decision_id}") + + def add_artifact(self, decision_id: str, artifact_id: str) -> None: + """Record that a decision produced an artifact.""" + decision = self._decision_repo.get_by_id(decision_id) + if decision: + updated = decision.with_artifact(artifact_id) + self._decision_repo.update(updated) + ``` + - [ ] Commit: "feat(service): implement downstream relationship updates" + - [ ] **D2.6b** [Hamza] Implement `mark_superseded()`: + ```python + def mark_superseded(self, decision_id: str, by_decision_id: str) -> None: + """Mark a decision as superseded by another.""" + decision = self._decision_repo.get_by_id(decision_id) + if not decision: + raise DecisionNotFoundError(decision_id) + + if decision.superseded_by: + raise AlreadySupersededError( + f"Decision {decision_id} already superseded by {decision.superseded_by}" + ) + + updated = decision.mark_superseded(by_decision_id) + self._decision_repo.update(updated) + logger.info(f"Marked decision {decision_id} as superseded by {by_decision_id}") + ``` + - [ ] Commit: "feat(service): implement mark_superseded()" + - [ ] Tests: Verify decision tree is built during Strategize + - [ ] **D2.7** [Rui] Write Behave scenarios in `features/decision_recording.feature`: + - [ ] **D2.7a** [Rui] Basic recording scenarios: + - [ ] Scenario: Record first decision creates root + - [ ] Given a plan "plan-123" with no decisions + - [ ] When I call record_decision with decision_type=PROMPT_DEFINITION + - [ ] Then a Decision is created with sequence_number=0 + - [ ] And parent_decision_id is None + - [ ] And is_root returns True + - [ ] Scenario: Record subsequent decisions increment sequence + - [ ] Given a plan with 2 existing decisions + - [ ] When I record another decision + - [ ] Then sequence_number is 2 + - [ ] Commit: "test(behave): add basic decision recording scenarios" + - [ ] **D2.7b** [Rui] Tree structure scenarios: + - [ ] Scenario: Child decision links to parent + - [ ] Given root decision D1 exists + - [ ] When I record decision D2 with parent_decision_id=D1.id + - [ ] Then D1.downstream_decision_ids contains D2.id + - [ ] Scenario: Get decision tree returns correct order + - [ ] Given decisions D1, D2, D3 with sequences 0, 1, 2 + - [ ] When I call get_decision_tree(plan_id) + - [ ] Then decisions are returned in sequence order + - [ ] Scenario: Get children returns direct children only + - [ ] Given D1 -> D2 -> D3 (D2 child of D1, D3 child of D2) + - [ ] When I call get_children(D1.id) + - [ ] Then only D2 is returned (not D3) + - [ ] Commit: "test(behave): add decision tree structure scenarios" + - [ ] **D2.7c** [Rui] Context snapshot scenarios: + - [ ] Scenario: Context snapshot captured with decision + - [ ] Given hot context "file contents..." + - [ ] When I record a decision + - [ ] Then decision.context_snapshot is not None + - [ ] And context_snapshot.hot_context_hash is valid SHA-256 + - [ ] Scenario: Duplicate context reuses existing snapshot + - [ ] Given decision D1 with context hash "abc123" + - [ ] When I record D2 with identical context + - [ ] Then D2.context_snapshot.snapshot_id differs from D1 + - [ ] But content is only stored once (deduplication) + - [ ] Commit: "test(behave): add context snapshot scenarios" + - [ ] **D2.7d** [Rui] Downstream relationship scenarios: + - [ ] Scenario: Subplan spawn updates downstream_plan_ids + - [ ] Given decision D1 of type SUBPLAN_SPAWN + - [ ] When subplan SP1 is created from D1 + - [ ] And add_downstream_plan(D1.id, SP1.id) is called + - [ ] Then D1.downstream_plan_ids contains SP1.id + - [ ] Scenario: Artifact production updates artifacts_produced + - [ ] Given decision D1 produces artifact A1 + - [ ] When add_artifact(D1.id, A1.id) is called + - [ ] Then D1.artifacts_produced contains A1.id + - [ ] Commit: "test(behave): add downstream relationship scenarios" -**Parallel Group D3: Decision CLI & Viewing [Hamza + Rui]** (depends on D1/D2) -- [ ] **COMMIT (Owner: Hamza | Group: D3.cli) - Commit message: "feat(cli): add plan tree and explain commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement `plan tree` and `plan explain` with rich/json/flat formats and `--show-superseded`/`--show-context`. - - [ ] Code [Hamza]: Add `--show-reasoning` to include confidence and alternatives in explain output per spec. - - [ ] Docs [Hamza]: Update CLI reference for decision viewing commands. - - [ ] Tests (Behave) [Hamza]: Add tree/explain scenarios including superseded handling. - - [ ] Tests (Robot) [Hamza]: Add `robot/decision_cli.robot` smoke tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/decision_cli_bench.py` for tree rendering overhead. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(cli): add plan tree and explain commands"`. +- [ ] **Stage D3: Decision CLI & Viewing** (Day 16-17) **[Hamza]** + + **SEQUENTIAL ORDER**: D3.1 (tree command) → D3.2 (explain command) → D3.3 (JSON output) → D3.4 (guidance-file) → D3.5 (Tests) + + - [ ] Code: Decision viewing commands + - [ ] **D3.1** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan tree [plan_id]`: + - [ ] **D3.1a** [Hamza] Create command in `src/cleveragents/cli/commands/plan.py`: + ```python + @plan.command("tree") + @click.argument("plan_id", required=False) + @click.option("--format", "output_format", + type=click.Choice(["tree", "json", "flat"]), default="tree") + @click.option("--show-superseded", is_flag=True, + help="Include superseded decisions") + def show_tree(plan_id: str | None, output_format: str, show_superseded: bool): + """Display the decision tree for a plan.""" + ``` + - [ ] Commit: "feat(cli): add plan tree command signature" + - [ ] **D3.1b** [Hamza] Implement plan resolution: + ```python + # If no plan_id, use current/most recent plan + if not plan_id: + plan = plan_service.get_current_plan() + if not plan: + console.print("[red]No active plan. Specify a plan ID.[/red]") + raise SystemExit(1) + plan_id = plan.plan_id + + # Fetch decision tree + decisions = decision_service.get_decision_tree(plan_id) + if not decisions: + console.print(f"[yellow]No decisions recorded for plan {plan_id}[/yellow]") + return + ``` + - [ ] Commit: "feat(cli): implement plan resolution for tree command" + - [ ] **D3.1c** [Hamza] Implement tree rendering with Rich: + ```python + def _render_decision_tree(decisions: list[Decision], show_superseded: bool): + """Render decision tree using Rich Tree.""" + from rich.tree import Tree + from rich.text import Text + + # Build tree structure + root_decisions = [d for d in decisions if d.is_root] + by_parent: dict[str, list[Decision]] = {} + for d in decisions: + if d.parent_decision_id: + by_parent.setdefault(d.parent_decision_id, []).append(d) + + # Create Rich tree + tree = Tree("[bold]Decision Tree[/bold]") + + def add_node(parent_tree, decision: Decision): + # Format decision display + type_color = _get_type_color(decision.decision_type) + label = Text() + label.append(f"[{decision.decision_type.value}] ", style=type_color) + label.append(f'"{decision.question[:40]}..."' if len(decision.question) > 40 else f'"{decision.question}"') + + if decision.confidence_score: + label.append(f" (conf: {decision.confidence_score:.2f})", style="dim") + + # Mark superseded + if decision.superseded_by: + if not show_superseded: + return + label.stylize("strike dim") + label.append(" [SUPERSEDED]", style="yellow") + + # Mark corrections + if decision.is_correction: + label.append(" [CORRECTION]", style="green") + + # Add subplan links + for subplan_id in decision.downstream_plan_ids: + label.append(f" → {subplan_id[:8]}", style="cyan") + + node = parent_tree.add(label) + + # Add children recursively + for child in by_parent.get(decision.decision_id, []): + add_node(node, child) + + for root in root_decisions: + add_node(tree, root) + + console.print(tree) + ``` + - [ ] Commit: "feat(cli): implement tree rendering with Rich" + - [ ] **D3.1d** [Hamza] Add type-specific coloring: + ```python + def _get_type_color(decision_type: DecisionType) -> str: + """Get color for decision type.""" + colors = { + DecisionType.PROMPT_DEFINITION: "bold white", + DecisionType.STRATEGY_CHOICE: "blue", + DecisionType.IMPLEMENTATION_CHOICE: "cyan", + DecisionType.RESOURCE_SELECTION: "magenta", + DecisionType.SUBPLAN_SPAWN: "green", + DecisionType.TOOL_INVOCATION: "yellow", + DecisionType.ERROR_RECOVERY: "red", + DecisionType.VALIDATION_RESPONSE: "orange3", + DecisionType.USER_INTERVENTION: "bold yellow", + } + return colors.get(decision_type, "white") + ``` + - [ ] Commit: "feat(cli): add decision type coloring" + - [ ] **D3.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan explain `: + - [ ] **D3.2a** [Hamza] Create command signature: + ```python + @plan.command("explain") + @click.argument("decision_id") + @click.option("--show-context", is_flag=True, help="Show full context snapshot") + @click.option("--show-reasoning", is_flag=True, help="Show raw LLM reasoning") + def explain_decision(decision_id: str, show_context: bool, show_reasoning: bool): + """Show detailed explanation of a specific decision.""" + ``` + - [ ] Commit: "feat(cli): add plan explain command signature" + - [ ] **D3.2b** [Hamza] Implement detailed display: + ```python + decision = decision_service.get_decision(decision_id) + if not decision: + console.print(f"[red]Decision {decision_id} not found[/red]") + raise SystemExit(1) + + # Create panels for display + from rich.panel import Panel + from rich.table import Table + + # Header panel + header = Panel( + f"[bold]Decision: {decision.decision_id}[/bold]\n" + f"Type: {decision.decision_type.value}\n" + f"Plan: {decision.plan_id}", + title="Decision Details" + ) + console.print(header) + + # Question and answer + console.print(f"\n[bold]Question:[/bold] {decision.question}") + console.print(f"\n[bold green]Chosen:[/bold green] {decision.chosen_option}") + + # Alternatives + if decision.alternatives_considered: + console.print("\n[bold]Alternatives Considered:[/bold]") + for alt in decision.alternatives_considered: + console.print(f" • {alt}") + + # Confidence and rationale + if decision.confidence_score is not None: + bar = "█" * int(decision.confidence_score * 10) + "░" * (10 - int(decision.confidence_score * 10)) + console.print(f"\n[bold]Confidence:[/bold] {decision.confidence_score:.2f} [{bar}]") + + if decision.rationale: + console.print(f"\n[bold]Rationale:[/bold] {decision.rationale}") + ``` + - [ ] Commit: "feat(cli): implement explain decision display" + - [ ] **D3.2c** [Hamza] Show upstream/downstream relationships: + ```python + # Upstream (ancestors) + ancestors = decision_service.get_ancestors(decision_id) + if ancestors: + console.print("\n[bold]Decision Path (what led here):[/bold]") + for i, anc in enumerate(reversed(ancestors)): + indent = " " * i + console.print(f"{indent}↳ [{anc.decision_type.value}] {anc.question[:50]}") + + # Downstream impact + children = decision_service.get_children(decision_id) + if children or decision.downstream_plan_ids: + console.print("\n[bold]Downstream Impact:[/bold]") + if children: + console.print(f" • {len(children)} child decisions") + if decision.downstream_plan_ids: + console.print(f" • {len(decision.downstream_plan_ids)} subplans spawned:") + for sp_id in decision.downstream_plan_ids: + console.print(f" - {sp_id}") + if decision.artifacts_produced: + console.print(f" • {len(decision.artifacts_produced)} artifacts produced") + ``` + - [ ] Commit: "feat(cli): add upstream/downstream display" + - [ ] **D3.2d** [Hamza] Add context and reasoning display: + ```python + # Context snapshot + if show_context: + console.print("\n[bold]Context Snapshot:[/bold]") + console.print(f" Hash: {decision.context_snapshot.hot_context_hash[:16]}...") + console.print(f" Resources: {', '.join(decision.context_snapshot.relevant_resources)}") + + # Optionally show full content + try: + _, content = snapshot_store.retrieve(decision.context_snapshot.snapshot_id) + console.print(Panel(content[:1000] + "..." if len(content) > 1000 else content, + title="Context Content")) + except Exception as e: + console.print(f" [dim]Content not available: {e}[/dim]") + + # Raw LLM reasoning + if show_reasoning and decision.actor_reasoning: + console.print(Panel(decision.actor_reasoning, title="LLM Reasoning")) + ``` + - [ ] Commit: "feat(cli): add context and reasoning display" + - [ ] **D3.3** [Hamza] Implement JSON output: + - [ ] **D3.3a** [Hamza] Add JSON format to tree command: + ```python + if output_format == "json": + # Build JSON structure + def decision_to_dict(d: Decision) -> dict: + return { + "decision_id": d.decision_id, + "type": d.decision_type.value, + "question": d.question, + "chosen_option": d.chosen_option, + "alternatives": d.alternatives_considered, + "confidence": d.confidence_score, + "rationale": d.rationale, + "parent_id": d.parent_decision_id, + "sequence": d.sequence_number, + "is_correction": d.is_correction, + "superseded_by": d.superseded_by, + "downstream_decisions": d.downstream_decision_ids, + "downstream_plans": d.downstream_plan_ids, + "created_at": d.created_at.isoformat() + } + + tree_json = { + "plan_id": plan_id, + "decision_count": len(decisions), + "decisions": [decision_to_dict(d) for d in decisions] + } + + print(json.dumps(tree_json, indent=2)) + ``` + - [ ] Commit: "feat(cli): add JSON output for plan tree" + - [ ] **D3.3b** [Hamza] Add flat format (for scripting): + ```python + if output_format == "flat": + # Tab-separated values for easy parsing + print("ID\tTYPE\tSEQ\tPARENT\tQUESTION") + for d in decisions: + print(f"{d.decision_id}\t{d.decision_type.value}\t{d.sequence_number}\t" + f"{d.parent_decision_id or '-'}\t{d.question[:50]}") + ``` + - [ ] Commit: "feat(cli): add flat output for plan tree" + - [ ] **D3.4** [Hamza] Implement `--guidance-file` option: + - [ ] **D3.4a** [Hamza] Add option to plan correct command: + ```python + @plan.command("correct") + @click.argument("decision_id") + @click.option("--mode", type=click.Choice(["revert", "append"]), required=True) + @click.option("--guidance", "-g", help="Correction guidance text") + @click.option("--guidance-file", "-f", type=click.File('r'), + help="Read guidance from file (use - for stdin)") + @click.option("--dry-run", is_flag=True, help="Show impact without executing") + def correct_decision(decision_id, mode, guidance, guidance_file, dry_run): + """Correct a decision and re-execute affected work.""" + ``` + - [ ] Commit: "feat(cli): add guidance-file option to correct command" + - [ ] **D3.4b** [Hamza] Handle guidance source priority: + ```python + # Get guidance from appropriate source + if guidance_file: + guidance_text = guidance_file.read() + elif guidance: + guidance_text = guidance + else: + console.print("[red]Either --guidance or --guidance-file is required[/red]") + raise SystemExit(1) + + if not guidance_text.strip(): + console.print("[red]Guidance cannot be empty[/red]") + raise SystemExit(1) + ``` + - [ ] Commit: "feat(cli): implement guidance source handling" + - [ ] Tests: Behave scenarios for decision CLI + - [ ] **D3.5** [Rui] Write Behave scenarios in `features/decision_cli.feature`: + - [ ] **D3.5a** [Rui] Tree command scenarios: + - [ ] Scenario: Display decision tree for plan + - [ ] Given plan with 5 decisions in tree structure + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan tree {plan_id}` + - [ ] Then output shows tree with all decisions + - [ ] And decisions are color-coded by type + - [ ] Scenario: Tree command with JSON format + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan tree {plan_id} --format=json` + - [ ] Then output is valid JSON + - [ ] And JSON contains all decision fields + - [ ] Scenario: Tree hides superseded by default + - [ ] Given decision D1 superseded by D1' + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan tree` + - [ ] Then D1 is not shown + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan tree --show-superseded` + - [ ] Then D1 is shown with strikethrough + - [ ] Commit: "test(behave): add tree command scenarios" + - [ ] **D3.5b** [Rui] Explain command scenarios: + - [ ] Scenario: Explain shows full decision details + - [ ] Given decision D1 with all fields populated + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan explain {D1.id}` + - [ ] Then output shows question, chosen option, alternatives + - [ ] And output shows confidence and rationale + - [ ] Scenario: Explain shows upstream path + - [ ] Given decision D3 with ancestors D1 -> D2 -> D3 + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan explain {D3.id}` + - [ ] Then output shows "Decision Path" section + - [ ] And D1 and D2 are listed as ancestors + - [ ] Scenario: Explain shows downstream impact + - [ ] Given decision D1 with 2 child decisions and 1 subplan + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan explain {D1.id}` + - [ ] Then output shows "Downstream Impact" section + - [ ] And shows "2 child decisions" and "1 subplan" + - [ ] Commit: "test(behave): add explain command scenarios" + - [ ] **D3.5c** [Rui] Guidance file scenarios: + - [ ] Scenario: Read guidance from file + - [ ] Given guidance file with text "Fix the authentication bug" + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan correct {id} --mode=append --guidance-file guidance.txt` + - [ ] Then correction uses the file content as guidance + - [ ] Scenario: Read guidance from stdin + - [ ] When I run `echo "Fix bug" | agents [--data-dir PATH] [--config-path PATH] plan correct {id} --mode=append --guidance-file=-` + - [ ] Then correction uses stdin content as guidance + - [ ] Commit: "test(behave): add guidance file scenarios" -**Parallel Group D4: Decision Correction [Jeff + Luis]** (depends on D2/D3) -- [ ] **COMMIT (Owner: Jeff | Group: D4.revert) - Commit message: "feat(service): add decision correction revert flow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement correction impact analysis and dry-run reporting. - - [ ] Code [Jeff]: Revert flow with checkpoint rollback, supersede downstream decisions, and subtree re-exec. - - [ ] Code [Jeff]: Persist correction attempt IDs and link them to superseded decisions. - - [ ] Docs [Jeff]: Add `docs/reference/decision_correction.md` for revert behavior. - - [ ] Tests (Behave) [Jeff]: Add revert + dry-run scenarios. - - [ ] Tests (Robot) [Jeff]: Add revert integration tests with checkpoint rollback. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/decision_correction_revert_bench.py` for correction overhead. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(service): add decision correction revert flow"`. -- [ ] **COMMIT (Owner: Jeff | Group: D4.append) - Commit message: "feat(service): add decision correction append flow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Append flow creating fix subplan without rewriting history; link correction attempt + decision tree updates. - - [ ] Code [Jeff]: Record append corrections as separate subtree with explicit lineage. - - [ ] Docs [Jeff]: Extend correction docs for append mode and guidance-file usage. - - [ ] Tests (Behave) [Jeff]: Add append correction scenarios. - - [ ] Tests (Robot) [Jeff]: Add append correction smoke test. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/decision_correction_append_bench.py` for append overhead. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(service): add decision correction append flow"`. +- [ ] **Stage D4: Decision Correction Mechanism** (Day 17-19) **[Jeff - CRITICAL FOR 30-DAY GOAL]** + + **IMPORTANCE**: This is the core mechanism that enables large project autonomy. Without decision correction, any mistake requires restarting from scratch. With it, users can guide the system to correct specific decisions and only recompute affected work. + + **SEQUENTIAL ORDER**: D4.1 (Service) → D4.2 (Sandbox Checkpoints) → D4.3 (Re-execution) → D4.4-D4.5 (CLI) + + - [ ] Code: Implement decision correction (core to large project autonomy) + - [ ] **D4.1** [Jeff] Implement correction service in `src/cleveragents/application/services/correction_service.py`: + - [ ] **D4.1a** [Jeff] Create service scaffold and types: + - [ ] Import necessary domain models (Decision, Plan, DecisionType) + - [ ] Import repositories (DecisionRepository, LifecyclePlanRepository) + - [ ] Define `CorrectionResult` dataclass: + - [ ] `success: bool` - Whether correction succeeded + - [ ] `correction_attempt_id: str` - ULID of the correction attempt + - [ ] `new_decision_id: str | None` - ID of new decision (for revert mode) + - [ ] `subplan_id: str | None` - ID of fix subplan (for append mode) + - [ ] `affected_decisions: list[str]` - IDs of invalidated decisions + - [ ] `affected_plans: list[str]` - IDs of invalidated subplans + - [ ] `affected_artifacts: list[str]` - IDs of invalidated artifacts + - [ ] `error: str | None` - Error message if failed + - [ ] Define `ImpactAnalysis` dataclass: + - [ ] `decision_id: str` - Decision being analyzed + - [ ] `downstream_decisions: list[str]` - All affected decisions + - [ ] `downstream_plans: list[str]` - All affected subplans + - [ ] `downstream_artifacts: list[str]` - All affected artifacts + - [ ] `total_tokens_to_recompute: int | None` - Estimated cost + - [ ] Create `CorrectionService` class with DI for repositories + - [ ] Commit: "feat(correction): add CorrectionService scaffold and types" + - [ ] **D4.1b** [Jeff] Implement `correct_decision_revert(decision_id: str, guidance: str) -> CorrectionResult`: + - [ ] **Step 1: Validation** + - [ ] Fetch decision from repository + - [ ] Raise `DecisionNotFoundError` if not exists + - [ ] Fetch parent plan + - [ ] Raise `PlanNotCorrectableError` if plan.phase == APPLIED + - [ ] Raise `PlanNotCorrectableError` if decision.superseded_by is not None (already corrected) + - [ ] **Step 2: Impact Analysis** + - [ ] Call `identify_downstream_impact(decision_id)` to find all affected entities + - [ ] Log: "Correction will affect {n} decisions, {m} subplans, {k} artifacts" + - [ ] **Step 3: Create Correction Attempt Record** + - [ ] Generate new ULID for correction_attempt_id + - [ ] Create `correction_attempts` record with: + - [ ] `attempt_id = correction_attempt_id` + - [ ] `plan_id = decision.plan_id` + - [ ] `original_decision_id = decision_id` + - [ ] `status = 'pending'` + - [ ] `guidance = guidance` + - [ ] `created_at = now()` + - [ ] Persist to database + - [ ] **Step 4: Archive Old Subtree** + - [ ] For each affected decision: + - [ ] Create copy in `archived_decisions` table with original values + - [ ] Store reference to correction_attempt_id + - [ ] For each affected artifact: + - [ ] Move file to archive location + - [ ] Update artifact record with archive path + - [ ] Log: "Archived {n} decisions and {k} artifacts" + - [ ] **Step 5: Invalidate Old Decisions** + - [ ] For each affected decision starting from decision_id: + - [ ] Set `superseded_by = None` (will be filled when new decision created) + - [ ] Mark in execution_log that decision is invalidated + - [ ] For each affected subplan: + - [ ] Set state to CANCELLED + - [ ] Rollback subplan sandboxes + - [ ] **Step 6: Create Correction Decision** + - [ ] Create new Decision with: + - [ ] `decision_id = new ULID` + - [ ] `plan_id = original.plan_id` + - [ ] `parent_decision_id = original.parent_decision_id` (same parent) + - [ ] `sequence_number = original.sequence_number` (replaces in sequence) + - [ ] `decision_type = original.decision_type` + - [ ] `is_correction = True` + - [ ] `corrects_decision_id = decision_id` + - [ ] `question = original.question` + - [ ] `chosen_option = guidance` (user's correction) + - [ ] `rationale = f"User correction: {guidance}"` + - [ ] Update original decision: `superseded_by = new_decision_id` + - [ ] Persist new decision + - [ ] **Step 7: Rollback Sandbox** + - [ ] Get checkpoint_id from original decision's context_snapshot + - [ ] Call `sandbox_manager.rollback_to_checkpoint(plan_id, checkpoint_id)` + - [ ] This restores sandbox state to before the decision was made + - [ ] **Step 8: Re-execute from Decision Point** + - [ ] Build re-execution context: + - [ ] Include all decisions up to (but not including) the corrected one + - [ ] Include the new correction decision + - [ ] Include the guidance as additional context + - [ ] Call appropriate phase handler: + - [ ] If decision was in Strategize: resume strategy actor + - [ ] If decision was in Execute: resume execution actor + - [ ] Let actor generate new downstream decisions + - [ ] Continue normal phase flow + - [ ] **Step 9: Finalize** + - [ ] Update correction_attempt: `status = 'completed'`, `new_decision_id = new_decision.decision_id` + - [ ] Increment plan.attempt counter + - [ ] Return CorrectionResult with all IDs and counts + - [ ] **Error Handling** + - [ ] If any step fails, update correction_attempt: `status = 'failed'`, `error = message` + - [ ] Do NOT rollback the archive (preserve for debugging) + - [ ] Return CorrectionResult with success=False, error message + - [ ] Commit: "feat(correction): implement correct_decision_revert()" + - [ ] **D4.1c** [Jeff] Implement `correct_decision_append(decision_id: str, guidance: str) -> CorrectionResult`: + - [ ] **Step 1: Validation** (same as revert, but less strict) + - [ ] Fetch decision and plan + - [ ] Raise error if plan already Applied (can't modify) + - [ ] **Step 2: Create Fix Subplan** + - [ ] Create new action (or use built-in "fix" action) with: + - [ ] Description based on guidance + - [ ] Target resources from original decision's scope + - [ ] Use PlanLifecycleService.use_action() to create subplan + - [ ] Set subplan.parent_plan_id to original plan + - [ ] Set subplan's prompt to include: + - [ ] Original decision context + - [ ] What went wrong (from guidance) + - [ ] Instructions to fix + - [ ] **Step 3: Link to Decision** + - [ ] Add subplan_id to original decision's downstream_plan_ids + - [ ] Create new decision record of type USER_INTERVENTION + - [ ] Store guidance and fix plan reference + - [ ] **Step 4: Execute Fix Plan** + - [ ] If automation level allows, start subplan execution + - [ ] Otherwise, return subplan_id for manual execution + - [ ] Return CorrectionResult with subplan_id + - [ ] Commit: "feat(correction): implement correct_decision_append()" + - [ ] **D4.1d** [Jeff] Implement `identify_downstream_impact(decision_id: str) -> ImpactAnalysis`: + - [ ] **Recursive Decision Collection** + - [ ] Start with decision_id + - [ ] Query all decisions where parent_decision_id = current + - [ ] Recursively process each child + - [ ] Also check downstream_decision_ids relationship (DAG) + - [ ] Collect all IDs in depth-first order + - [ ] **Subplan Collection** + - [ ] For each decision, check if decision_type == SUBPLAN_SPAWN + - [ ] If so, add downstream_plan_ids to affected plans + - [ ] Recursively get that subplan's decisions too + - [ ] **Artifact Collection** + - [ ] For each affected decision, get artifacts_produced list + - [ ] Deduplicate (same artifact may be referenced multiple times) + - [ ] **Cost Estimation** (optional) + - [ ] Estimate tokens by summing context sizes of affected decisions + - [ ] This helps user decide if correction is worth it + - [ ] Return ImpactAnalysis with all collected data + - [ ] Commit: "feat(correction): implement identify_downstream_impact()" + - [ ] **D4.2** [Jeff] Implement sandbox checkpointing for correction: + - [ ] **D4.2a** [Jeff] Extend SandboxManager to track checkpoints: + - [ ] Add `create_checkpoint(plan_id: str, label: str) -> str`: + - [ ] For git sandboxes: commit current state with checkpoint tag + - [ ] For filesystem sandboxes: snapshot directory (or use git worktree trick) + - [ ] Return checkpoint_id + - [ ] Add `list_checkpoints(plan_id: str) -> list[Checkpoint]`: + - [ ] Return all checkpoints for the plan in chronological order + - [ ] Commit: "feat(sandbox): add checkpoint creation to SandboxManager" + - [ ] **D4.2b** [Jeff] Store checkpoint ID with each decision: + - [ ] Update Decision model: add `checkpoint_id: str | None` field + - [ ] When decision is created, auto-create checkpoint + - [ ] Store checkpoint_id in decision record + - [ ] Commit: "feat(decision): track checkpoint_id per decision" + - [ ] **D4.2c** [Jeff] Implement `rollback_to_checkpoint(plan_id: str, checkpoint_id: str) -> None`: + - [ ] Find all sandboxes for plan_id + - [ ] For each sandbox: + - [ ] If git: `git reset --hard {checkpoint_tag}` + - [ ] If filesystem: restore from snapshot + - [ ] Clear any state beyond checkpoint + - [ ] Update sandbox status + - [ ] Commit: "feat(sandbox): implement rollback_to_checkpoint()" + - [ ] **D4.2d** [Jeff] Handle checkpoint cleanup: + - [ ] After successful apply, old checkpoints can be pruned + - [ ] Keep at least N most recent checkpoints for debugging + - [ ] Implement `prune_checkpoints(plan_id: str, keep_count: int) -> int` + - [ ] Commit: "feat(sandbox): implement checkpoint pruning" + - [ ] **D4.3** [Jeff] Implement re-execution from correction point: + - [ ] **D4.3a** [Jeff] Build context for resumed execution: + - [ ] Create `CorrectionContext` dataclass: + - [ ] `original_decision: Decision` - What was being corrected + - [ ] `correction_decision: Decision` - The new corrected decision + - [ ] `guidance: str` - User's correction text + - [ ] `prior_decisions: list[Decision]` - Decisions that remain valid + - [ ] `invalidated_decisions: list[Decision]` - For reference/diff + - [ ] Commit: "feat(correction): define CorrectionContext" + - [ ] **D4.3b** [Jeff] Inject correction context into actor: + - [ ] Update strategy/execution actor invocation to accept CorrectionContext + - [ ] Actor prompt includes: + - [ ] "You previously decided: {original_decision.chosen_option}" + - [ ] "This decision is being corrected because: {guidance}" + - [ ] "Please reconsider and make a new decision based on this feedback." + - [ ] Actor should acknowledge correction and proceed + - [ ] Commit: "feat(correction): inject correction context into actor" + - [ ] **D4.3c** [Jeff] Resume phase execution from decision point: + - [ ] If correction is in Strategize phase: + - [ ] Resume strategy actor from decision point + - [ ] Actor generates new downstream decisions + - [ ] Continue until Strategize complete + - [ ] If correction is in Execute phase: + - [ ] Resume execution actor + - [ ] Regenerate affected subplans + - [ ] Continue execution + - [ ] Normal Apply phase follows + - [ ] Commit: "feat(correction): implement re-execution from decision point" + - [ ] **D4.3d** [Jeff] Handle correction failures: + - [ ] If actor fails during re-execution: + - [ ] Update correction_attempt status to 'failed' + - [ ] Preserve both old and new state for debugging + - [ ] Allow user to try different guidance + - [ ] Max correction attempts per decision: 3 (configurable) + - [ ] Commit: "feat(correction): handle re-execution failures" + - [ ] **D4.4** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan correct --mode=revert --guidance ""`: + - [ ] **D4.4a** [Hamza] Create correction command in plan CLI: + - [ ] Add `@plan.command("correct")` with Click + - [ ] Required argument: `decision_id: str` + - [ ] Required option: `--mode: str` (choices: revert, append) + - [ ] Required option: `--guidance: str` or `--guidance-file: Path` + - [ ] Optional: `--dry-run` to show impact without executing + - [ ] Commit: "feat(cli): add plan correct command scaffold" + - [ ] **D4.4b** [Hamza] Implement command logic for revert mode: + - [ ] Validate decision_id format (ULID) + - [ ] If --dry-run: + - [ ] Call CorrectionService.identify_downstream_impact() + - [ ] Display impact analysis + - [ ] Exit without making changes + - [ ] Show confirmation prompt: "This will affect {n} decisions. Continue? [y/N]" + - [ ] Support `--yes` to bypass confirmation + - [ ] If confirmed (or --yes), call CorrectionService.correct_decision_revert() + - [ ] Display progress with Rich console: + - [ ] "[1/5] Analyzing impact..." + - [ ] "[2/5] Archiving old decisions..." + - [ ] "[3/5] Rolling back sandbox..." + - [ ] "[4/5] Re-executing from decision point..." + - [ ] "[5/5] Finalizing correction..." + - [ ] On success, display: + - [ ] New decision ID + - [ ] Count of regenerated decisions + - [ ] Diff summary (files changed old vs new) + - [ ] On failure, display error with recovery suggestions + - [ ] Commit: "feat(cli): implement plan correct revert mode" + - [ ] **D4.4c** [Hamza] Handle guidance-file option: + - [ ] If --guidance-file specified: + - [ ] Read file contents as guidance + - [ ] Support `-` for stdin: `cat guidance.txt | agents [--data-dir PATH] [--config-path PATH] plan correct ... --guidance-file=-` + - [ ] Validate guidance is not empty + - [ ] Commit: "feat(cli): add guidance-file support to plan correct" + - [ ] **D4.5** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan correct --mode=append --guidance ""`: + - [ ] **D4.5a** [Hamza] Implement append mode command: + - [ ] No confirmation needed (additive, not destructive) + - [ ] Call CorrectionService.correct_decision_append() + - [ ] Display created subplan ID + - [ ] If automation allows, show execution progress + - [ ] Otherwise show: "Fix subplan created: {subplan_id}. Run `agents [--data-dir PATH] [--config-path PATH] plan execute {subplan_id}` to apply fix." + - [ ] Commit: "feat(cli): implement plan correct append mode" + - [ ] Tests: Correction mechanism tests (CRITICAL for 30-day goal) + - [ ] **D4.6** [Rui] Write Behave scenarios in `features/decision_correction.feature`: + - [ ] **D4.6a** [Rui] Revert mode basic scenarios: + - [ ] Scenario: Correct early decision re-executes downstream work + - [ ] Given a plan with 3 decisions (D1 → D2 → D3) in sequence + - [ ] And D1 chose "use PostgreSQL" with downstream D2 choosing "use psycopg2" + - [ ] When I correct D1 with guidance "use SQLite instead" + - [ ] Then D1 is marked superseded + - [ ] And a new D1' is created with chosen_option "use SQLite" + - [ ] And D2, D3 are invalidated and regenerated + - [ ] And new D2' reflects SQLite (e.g., "use sqlite3") + - [ ] And the correction_attempt record shows status='completed' + - [ ] Commit: "test(behave): add basic revert correction scenario" + - [ ] **D4.6b** [Rui] Subplan invalidation scenarios: + - [ ] Scenario: Correct decision with subplans invalidates subplans + - [ ] Given a plan where D2 is a SUBPLAN_SPAWN decision + - [ ] And subplan SP1 was created from D2 + - [ ] And SP1 has completed some work + - [ ] When I correct D2 with new guidance + - [ ] Then SP1 is marked as CANCELLED + - [ ] And SP1's sandbox is rolled back + - [ ] And a new subplan SP2 is created based on new guidance + - [ ] And SP2 uses the corrected context + - [ ] Commit: "test(behave): add subplan invalidation correction scenario" + - [ ] **D4.6c** [Rui] Append mode scenarios: + - [ ] Scenario: Append mode creates fix subplan without modifying history + - [ ] Given a plan with D1, D2, D3 all completed + - [ ] And the outcome has a bug due to D2's decision + - [ ] When I correct D2 with mode=append and guidance "add error handling" + - [ ] Then D2 is NOT marked as superseded + - [ ] And a new fix subplan is created + - [ ] And the fix subplan's prompt includes the guidance + - [ ] And the fix subplan targets the same resources as D2 + - [ ] Commit: "test(behave): add append mode correction scenario" + - [ ] **D4.6d** [Rui] Safety and error scenarios: + - [ ] Scenario: Cannot correct decision in Applied plan + - [ ] Given a plan that has been Applied successfully + - [ ] When I try to correct any decision + - [ ] Then I receive error "Cannot correct decisions in an applied plan" + - [ ] And no changes are made + - [ ] Scenario: Cannot correct already-corrected decision + - [ ] Given decision D1 that was already corrected to D1' + - [ ] When I try to correct D1 again + - [ ] Then I receive error "Decision already superseded" + - [ ] And hint "Correct the replacement decision D1' instead" + - [ ] Commit: "test(behave): add correction safety scenarios" + - [ ] **D4.6e** [Rui] History preservation scenarios: + - [ ] Scenario: Correction preserves history for comparison + - [ ] Given I correct decision D1 with new guidance + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan tree {plan_id} --show-superseded` + - [ ] Then I see the original D1 decision details + - [ ] And I see the correction D1' decision details + - [ ] And I can compare the outcomes via `agents [--data-dir PATH] [--config-path PATH] plan diff --correction {plan_id}` + - [ ] Scenario: Archived artifacts are accessible + - [ ] Given correction archived some generated files + - [ ] Then I can retrieve archived files for diff comparison + - [ ] Commit: "test(behave): add history preservation scenarios" + - [ ] **D4.6f** [Rui] Dry-run and impact analysis scenarios: + - [ ] Scenario: Dry-run shows impact without making changes + - [ ] Given a plan with 5 decisions and 2 subplans + - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan correct D2 --mode=revert --guidance "..." --dry-run` + - [ ] Then I see "This will affect:" + - [ ] And I see "- 3 decisions" + - [ ] And I see "- 1 subplan" + - [ ] And I see "- Estimated recomputation: ~5000 tokens" + - [ ] And no changes are made to the plan + - [ ] Commit: "test(behave): add dry-run and impact analysis scenarios" -**Parallel Group D5: Decision Persistence [Hamza + Luis]** (depends on D1) -- [ ] **COMMIT (Owner: Hamza | Group: D5.db) - Commit message: "feat(db): add decision tables"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Add Alembic migrations for `decisions` and `context_snapshots` with indexes. - - [ ] Code [Hamza]: Add indexes for plan_id, decision_type, and superseded flags for fast tree queries. - - [ ] Docs [Hamza]: Update `docs/reference/database_schema.md` with decision tables. - - [ ] Tests (Behave) [Hamza]: Add migration verification scenarios. - - [ ] Tests (Robot) [Hamza]: Add DB migration smoke test. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/decision_migration_bench.py` for migration baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(db): add decision tables"`. -- [ ] **COMMIT (Owner: Hamza | Group: D5.repo) - Commit message: "feat(repo): add decision repositories"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement DecisionRepository + ContextSnapshotRepository with tree queries and max-sequence helpers. - - [ ] Code [Hamza]: Add repository methods for superseded decision lookup and subtree retrieval. - - [ ] Docs [Hamza]: Document repository interfaces in `docs/reference/repositories.md`. - - [ ] Tests (Behave) [Hamza]: Add decision persistence scenarios (create/query/superseded). - - [ ] Tests (Robot) [Hamza]: Add repository integration smoke test. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/decision_repository_bench.py` for tree query performance. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(repo): add decision repositories"`. -- [ ] **COMMIT (Owner: Luis | Group: D5.di) - Commit message: "feat(di): wire decision services"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Wire decision repositories + services into DI and CLI. - - [ ] Docs [Luis]: Update DI docs for decision wiring. - - [ ] Tests (Behave) [Luis]: Add DI wiring scenarios for decision commands. - - [ ] Tests (Robot) [Luis]: Add CLI smoke test using persisted decisions. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/decision_di_bench.py` for DI resolution overhead. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(di): wire decision services"`. -- [ ] **COMMIT (Owner: Rui | Group: D5.tests) - Commit message: "test(persistence): add decision persistence suites"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Tests (Behave) [Rui]: Add `features/decision_persistence.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add `robot/decision_persistence.robot` E2E coverage. - - [ ] Docs [Rui]: Update `docs/development/testing.md` with decision suites. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_persistence_bench.py` for DB persistence throughput. - - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "test(persistence): add decision persistence suites"`. +- [ ] **Stage D5: Decision Persistence** (Day 18-19) **[Hamza]** + + **SEQUENTIAL ORDER**: D5.1 (decisions table) → D5.2 (dependencies table) → D5.3 (correction_attempts) → D5.4 (context_snapshots) → D5.5 (DecisionModel) → D5.6 (DecisionRepository) → D5.7 (Tests) + + - [ ] Code: Decision database schema + - [ ] **D5.1** [Hamza] Create Alembic migration for `decisions` table: + - [ ] **D5.1a** [Hamza] Generate migration file: + - [ ] Run `alembic revision --autogenerate -m "create_decisions_table"` + - [ ] Commit: "chore(db): generate decisions table migration" + - [ ] **D5.1b** [Hamza] Define schema: + ```python + def upgrade(): + op.create_table( + 'decisions', + # Identity + sa.Column('decision_id', sa.Text(), nullable=False), + sa.Column('plan_id', sa.Text(), nullable=False), + + # Tree structure + sa.Column('parent_decision_id', sa.Text(), nullable=True), + sa.Column('sequence_number', sa.Integer(), nullable=False), + + # Decision content + sa.Column('decision_type', sa.Text(), nullable=False), + sa.Column('question', sa.Text(), nullable=False), + sa.Column('chosen_option', sa.Text(), nullable=False), + sa.Column('alternatives_considered', sa.JSON(), nullable=False, server_default='[]'), + sa.Column('confidence_score', sa.Float(), nullable=True), + sa.Column('rationale', sa.Text(), nullable=False, server_default=''), + sa.Column('actor_reasoning', sa.Text(), nullable=True), + + # Context + sa.Column('context_snapshot_id', sa.Text(), nullable=False), + sa.Column('checkpoint_id', sa.Text(), nullable=True), + + # Downstream relationships (denormalized for query performance) + sa.Column('downstream_decision_ids', sa.JSON(), nullable=False, server_default='[]'), + sa.Column('downstream_plan_ids', sa.JSON(), nullable=False, server_default='[]'), + sa.Column('artifacts_produced', sa.JSON(), nullable=False, server_default='[]'), + + # Correction tracking + sa.Column('is_correction', sa.Boolean(), nullable=False, server_default='false'), + sa.Column('corrects_decision_id', sa.Text(), nullable=True), + sa.Column('superseded_by', sa.Text(), nullable=True), + + # Timestamp + sa.Column('created_at', sa.Text(), nullable=False), + + # Constraints + sa.PrimaryKeyConstraint('decision_id'), + sa.ForeignKeyConstraint(['plan_id'], ['lifecycle_plans.plan_id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['parent_decision_id'], ['decisions.decision_id'], ondelete='SET NULL'), + sa.ForeignKeyConstraint(['context_snapshot_id'], ['context_snapshots.snapshot_id']), + ) + ``` + - [ ] Commit: "feat(db): add decisions table schema" + - [ ] **D5.1c** [Hamza] Add indices for query optimization: + ```python + # Indices for common queries + op.create_index('ix_decisions_plan_id', 'decisions', ['plan_id']) + op.create_index('ix_decisions_parent_id', 'decisions', ['parent_decision_id']) + op.create_index('ix_decisions_plan_sequence', 'decisions', ['plan_id', 'sequence_number']) + op.create_index('ix_decisions_type', 'decisions', ['decision_type']) + op.create_index('ix_decisions_superseded', 'decisions', ['superseded_by'], + postgresql_where=sa.text('superseded_by IS NOT NULL')) + ``` + - [ ] Commit: "feat(db): add decisions table indices" + - [ ] **D5.1d** [Hamza] Add downgrade: + ```python + def downgrade(): + op.drop_index('ix_decisions_superseded') + op.drop_index('ix_decisions_type') + op.drop_index('ix_decisions_plan_sequence') + op.drop_index('ix_decisions_parent_id') + op.drop_index('ix_decisions_plan_id') + op.drop_table('decisions') + ``` + - [ ] Commit: "feat(db): add decisions table downgrade" + - [ ] **D5.2** [Hamza] Create Alembic migration for `decision_dependencies` table: + - [ ] **D5.2a** [Hamza] Define schema for DAG relationships: + ```python + def upgrade(): + op.create_table( + 'decision_dependencies', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('upstream_decision_id', sa.Text(), nullable=False), + sa.Column('downstream_decision_id', sa.Text(), nullable=False), + sa.Column('dependency_type', sa.Text(), nullable=False), # 'data', 'ordering', 'spawned' + sa.Column('created_at', sa.Text(), nullable=False), + + sa.PrimaryKeyConstraint('id'), + sa.ForeignKeyConstraint(['upstream_decision_id'], ['decisions.decision_id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['downstream_decision_id'], ['decisions.decision_id'], ondelete='CASCADE'), + sa.UniqueConstraint('upstream_decision_id', 'downstream_decision_id', name='uq_decision_dependency') + ) + op.create_index('ix_dep_upstream', 'decision_dependencies', ['upstream_decision_id']) + op.create_index('ix_dep_downstream', 'decision_dependencies', ['downstream_decision_id']) + ``` + - [ ] Commit: "feat(db): add decision_dependencies table" + - [ ] **D5.2b** [Hamza] Add downgrade: + - [ ] Drop indices and table + - [ ] Commit: "feat(db): add decision_dependencies downgrade" + - [ ] **D5.3** [Hamza] Create Alembic migration for `correction_attempts` table: + - [ ] **D5.3a** [Hamza] Define schema: + ```python + def upgrade(): + op.create_table( + 'correction_attempts', + sa.Column('attempt_id', sa.Text(), nullable=False), + sa.Column('plan_id', sa.Text(), nullable=False), + sa.Column('original_decision_id', sa.Text(), nullable=False), + sa.Column('new_decision_id', sa.Text(), nullable=True), # Set when complete + sa.Column('mode', sa.Text(), nullable=False), # 'revert' or 'append' + sa.Column('guidance', sa.Text(), nullable=False), + sa.Column('status', sa.Text(), nullable=False), # pending, completed, failed + sa.Column('error_message', sa.Text(), nullable=True), + sa.Column('affected_decisions', sa.JSON(), nullable=False, server_default='[]'), + sa.Column('affected_plans', sa.JSON(), nullable=False, server_default='[]'), + sa.Column('created_at', sa.Text(), nullable=False), + sa.Column('completed_at', sa.Text(), nullable=True), + + sa.PrimaryKeyConstraint('attempt_id'), + sa.ForeignKeyConstraint(['plan_id'], ['lifecycle_plans.plan_id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['original_decision_id'], ['decisions.decision_id']), + sa.ForeignKeyConstraint(['new_decision_id'], ['decisions.decision_id']) + ) + op.create_index('ix_correction_plan', 'correction_attempts', ['plan_id']) + op.create_index('ix_correction_status', 'correction_attempts', ['status']) + ``` + - [ ] Commit: "feat(db): add correction_attempts table" + - [ ] **D5.3b** [Hamza] Add downgrade: + - [ ] Commit: "feat(db): add correction_attempts downgrade" + - [ ] **D5.4** [Hamza] Create Alembic migration for `context_snapshots` table: + - [ ] **D5.4a** [Hamza] Define schema: + ```python + def upgrade(): + op.create_table( + 'context_snapshots', + sa.Column('snapshot_id', sa.Text(), nullable=False), + sa.Column('hot_context_hash', sa.Text(), nullable=False), + sa.Column('hot_context_ref', sa.Text(), nullable=False), # File path or blob ID + sa.Column('relevant_resources', sa.JSON(), nullable=False, server_default='[]'), + sa.Column('actor_state_ref', sa.Text(), nullable=True), + sa.Column('file_versions', sa.JSON(), nullable=False, server_default='{}'), + sa.Column('content_size_bytes', sa.Integer(), nullable=False, server_default='0'), + sa.Column('created_at', sa.Text(), nullable=False), + + sa.PrimaryKeyConstraint('snapshot_id') + ) + # Index for content deduplication + op.create_index('ix_snapshot_hash', 'context_snapshots', ['hot_context_hash']) + ``` + - [ ] Commit: "feat(db): add context_snapshots table" + - [ ] **D5.4b** [Hamza] Add downgrade: + - [ ] Commit: "feat(db): add context_snapshots downgrade" + - [ ] **D5.5** [Hamza] Create `DecisionModel` in `src/cleveragents/infrastructure/database/models.py`: + - [ ] **D5.5a** [Hamza] Define SQLAlchemy model: + ```python + class DecisionModel(Base): + __tablename__ = 'decisions' + + decision_id = Column(Text, primary_key=True) + plan_id = Column(Text, ForeignKey('lifecycle_plans.plan_id', ondelete='CASCADE'), nullable=False) + parent_decision_id = Column(Text, ForeignKey('decisions.decision_id', ondelete='SET NULL'), nullable=True) + sequence_number = Column(Integer, nullable=False) + + decision_type = Column(Text, nullable=False) + question = Column(Text, nullable=False) + chosen_option = Column(Text, nullable=False) + alternatives_considered = Column(JSON, nullable=False, default=list) + confidence_score = Column(Float, nullable=True) + rationale = Column(Text, nullable=False, default='') + actor_reasoning = Column(Text, nullable=True) + + context_snapshot_id = Column(Text, ForeignKey('context_snapshots.snapshot_id'), nullable=False) + checkpoint_id = Column(Text, nullable=True) + + downstream_decision_ids = Column(JSON, nullable=False, default=list) + downstream_plan_ids = Column(JSON, nullable=False, default=list) + artifacts_produced = Column(JSON, nullable=False, default=list) + + is_correction = Column(Boolean, nullable=False, default=False) + corrects_decision_id = Column(Text, nullable=True) + superseded_by = Column(Text, nullable=True) + + created_at = Column(Text, nullable=False) + + # Relationships + plan = relationship("LifecyclePlanModel", back_populates="decisions") + parent = relationship("DecisionModel", remote_side=[decision_id], backref="children") + context_snapshot = relationship("ContextSnapshotModel") + ``` + - [ ] Commit: "feat(db): add DecisionModel SQLAlchemy class" + - [ ] **D5.5b** [Hamza] Add domain conversion methods: + ```python + def to_domain(self) -> Decision: + """Convert to domain model.""" + return Decision( + decision_id=self.decision_id, + plan_id=self.plan_id, + parent_decision_id=self.parent_decision_id, + sequence_number=self.sequence_number, + decision_type=DecisionType(self.decision_type), + question=self.question, + chosen_option=self.chosen_option, + alternatives_considered=self.alternatives_considered or [], + confidence_score=self.confidence_score, + rationale=self.rationale, + actor_reasoning=self.actor_reasoning, + context_snapshot=self.context_snapshot.to_domain(), + checkpoint_id=self.checkpoint_id, + downstream_decision_ids=self.downstream_decision_ids or [], + downstream_plan_ids=self.downstream_plan_ids or [], + artifacts_produced=self.artifacts_produced or [], + is_correction=self.is_correction, + corrects_decision_id=self.corrects_decision_id, + superseded_by=self.superseded_by, + created_at=datetime.fromisoformat(self.created_at) + ) + + @classmethod + def from_domain(cls, decision: Decision) -> "DecisionModel": + """Create from domain model.""" + return cls( + decision_id=decision.decision_id, + plan_id=decision.plan_id, + parent_decision_id=decision.parent_decision_id, + sequence_number=decision.sequence_number, + decision_type=decision.decision_type.value, + question=decision.question, + chosen_option=decision.chosen_option, + alternatives_considered=decision.alternatives_considered, + confidence_score=decision.confidence_score, + rationale=decision.rationale, + actor_reasoning=decision.actor_reasoning, + context_snapshot_id=decision.context_snapshot.snapshot_id, + checkpoint_id=decision.checkpoint_id, + downstream_decision_ids=decision.downstream_decision_ids, + downstream_plan_ids=decision.downstream_plan_ids, + artifacts_produced=decision.artifacts_produced, + is_correction=decision.is_correction, + corrects_decision_id=decision.corrects_decision_id, + superseded_by=decision.superseded_by, + created_at=decision.created_at.isoformat() + ) + ``` + - [ ] Commit: "feat(db): add DecisionModel conversion methods" + - [ ] **D5.6** [Hamza] Implement `DecisionRepository` in `src/cleveragents/infrastructure/database/repositories.py`: + - [ ] **D5.6a** [Hamza] Define repository class: + ```python + class DecisionRepository: + """Repository for Decision persistence.""" + + def __init__(self, session_factory: Callable[[], Session]): + self._session_factory = session_factory + ``` + - [ ] Commit: "feat(repo): add DecisionRepository scaffold" + - [ ] **D5.6b** [Hamza] Implement `create()`: + ```python + def create(self, decision: Decision) -> Decision: + """Persist a new decision.""" + with self._session_factory() as session: + model = DecisionModel.from_domain(decision) + session.add(model) + try: + session.commit() + except IntegrityError as e: + session.rollback() + if "FOREIGN KEY" in str(e): + raise PlanNotFoundError(decision.plan_id) + raise + return decision + ``` + - [ ] Commit: "feat(repo): implement DecisionRepository.create()" + - [ ] **D5.6c** [Hamza] Implement `get_by_id()`: + ```python + def get_by_id(self, decision_id: str) -> Decision | None: + """Get decision by ID.""" + with self._session_factory() as session: + model = session.query(DecisionModel).options( + joinedload(DecisionModel.context_snapshot) + ).filter_by(decision_id=decision_id).first() + return model.to_domain() if model else None + ``` + - [ ] Commit: "feat(repo): implement DecisionRepository.get_by_id()" + - [ ] **D5.6d** [Hamza] Implement `get_by_plan()`: + ```python + def get_by_plan(self, plan_id: str) -> list[Decision]: + """Get all decisions for a plan, ordered by sequence.""" + with self._session_factory() as session: + models = session.query(DecisionModel).options( + joinedload(DecisionModel.context_snapshot) + ).filter_by(plan_id=plan_id).order_by( + DecisionModel.sequence_number + ).all() + return [m.to_domain() for m in models] + ``` + - [ ] Commit: "feat(repo): implement DecisionRepository.get_by_plan()" + - [ ] **D5.6e** [Hamza] Implement `get_children()`: + ```python + def get_children(self, decision_id: str) -> list[Decision]: + """Get direct children of a decision.""" + with self._session_factory() as session: + models = session.query(DecisionModel).options( + joinedload(DecisionModel.context_snapshot) + ).filter_by(parent_decision_id=decision_id).order_by( + DecisionModel.sequence_number + ).all() + return [m.to_domain() for m in models] + ``` + - [ ] Commit: "feat(repo): implement DecisionRepository.get_children()" + - [ ] **D5.6f** [Hamza] Implement `get_tree()` with recursive CTE: + ```python + def get_tree(self, plan_id: str) -> list[Decision]: + """Get full decision tree for a plan using recursive CTE.""" + with self._session_factory() as session: + # Use recursive CTE for efficient tree retrieval + cte = session.query(DecisionModel).filter( + DecisionModel.plan_id == plan_id, + DecisionModel.parent_decision_id.is_(None) + ).cte(name='decision_tree', recursive=True) + + cte_alias = aliased(DecisionModel, cte) + recursive = session.query(DecisionModel).join( + cte_alias, DecisionModel.parent_decision_id == cte_alias.decision_id + ) + cte = cte.union_all(recursive) + + models = session.query(DecisionModel).select_from(cte).order_by( + DecisionModel.sequence_number + ).all() + return [m.to_domain() for m in models] + ``` + - [ ] Commit: "feat(repo): implement DecisionRepository.get_tree()" + - [ ] **D5.6g** [Hamza] Implement `get_downstream()`: + ```python + def get_downstream(self, decision_id: str) -> list[Decision]: + """Get all downstream decisions (recursive).""" + with self._session_factory() as session: + # Get the starting decision + start = session.query(DecisionModel).filter_by( + decision_id=decision_id + ).first() + if not start: + return [] + + # Recursively collect all downstream + result = [] + to_process = list(start.downstream_decision_ids) + seen = set() + + while to_process: + did = to_process.pop(0) + if did in seen: + continue + seen.add(did) + + d = session.query(DecisionModel).filter_by(decision_id=did).first() + if d: + result.append(d.to_domain()) + to_process.extend(d.downstream_decision_ids) + + return result + ``` + - [ ] Commit: "feat(repo): implement DecisionRepository.get_downstream()" + - [ ] **D5.6h** [Hamza] Implement `update()`: + ```python + def update(self, decision: Decision) -> Decision: + """Update an existing decision.""" + with self._session_factory() as session: + model = session.query(DecisionModel).filter_by( + decision_id=decision.decision_id + ).first() + if not model: + raise DecisionNotFoundError(decision.decision_id) + + # Update fields + model.downstream_decision_ids = decision.downstream_decision_ids + model.downstream_plan_ids = decision.downstream_plan_ids + model.artifacts_produced = decision.artifacts_produced + model.superseded_by = decision.superseded_by + + session.commit() + return decision + ``` + - [ ] Commit: "feat(repo): implement DecisionRepository.update()" + - [ ] **D5.6i** [Hamza] Implement `get_max_sequence()`: + ```python + def get_max_sequence(self, plan_id: str) -> int | None: + """Get maximum sequence number for a plan.""" + with self._session_factory() as session: + result = session.query(func.max(DecisionModel.sequence_number)).filter_by( + plan_id=plan_id + ).scalar() + return result + ``` + - [ ] Commit: "feat(repo): implement DecisionRepository.get_max_sequence()" + - [ ] **D5.6j** [Hamza] Add retry decorator to all methods: + - [ ] Same pattern as other repositories + - [ ] Commit: "feat(repo): add retry decorator to DecisionRepository" + - [ ] Tests: Integration tests for decision persistence + - [ ] **D5.7** [Rui] Write Behave scenarios in `features/decision_persistence.feature`: + - [ ] **D5.7a** [Rui] Basic persistence scenarios: + - [ ] Scenario: Decision persists with all fields + - [ ] Given a valid Decision domain object + - [ ] When I call decision_repo.create(decision) + - [ ] Then decision is stored in database + - [ ] And get_by_id returns the decision + - [ ] And all fields match original + - [ ] Scenario: Decision FK to plan enforced + - [ ] Given no plan with ID "nonexistent" + - [ ] When I try to create decision with that plan_id + - [ ] Then PlanNotFoundError is raised + - [ ] Commit: "test(behave): add basic decision persistence scenarios" + - [ ] **D5.7b** [Rui] Tree query scenarios: + - [ ] Scenario: get_by_plan returns decisions in sequence order + - [ ] Given plan with decisions at sequences 0, 1, 2 + - [ ] When I call get_by_plan(plan_id) + - [ ] Then decisions are returned in sequence order + - [ ] Scenario: get_tree returns full hierarchy + - [ ] Given plan with 3-level decision tree + - [ ] When I call get_tree(plan_id) + - [ ] Then all decisions are returned + - [ ] And tree structure is preserved + - [ ] Scenario: get_children returns only direct children + - [ ] Given D1 -> D2 -> D3 hierarchy + - [ ] When I call get_children(D1.id) + - [ ] Then only D2 is returned + - [ ] Commit: "test(behave): add decision tree query scenarios" + - [ ] **D5.7c** [Rui] Context snapshot scenarios: + - [ ] Scenario: Context snapshot stored and retrievable + - [ ] Given decision with context_snapshot + - [ ] When decision is persisted + - [ ] Then context_snapshot_id is stored + - [ ] And snapshot can be retrieved by ID + - [ ] Scenario: Snapshot deduplication by hash + - [ ] Given two decisions with identical context content + - [ ] Then only one snapshot is stored + - [ ] And both decisions reference same snapshot + - [ ] Commit: "test(behave): add context snapshot persistence scenarios" + - [ ] **D5.7d** [Rui] Correction tracking scenarios: + - [ ] Scenario: Correction attempt persists + - [ ] Given correction attempt with all fields + - [ ] When persisted via CorrectionAttemptRepository + - [ ] Then can be retrieved by attempt_id + - [ ] And status can be updated + - [ ] Scenario: superseded_by updates correctly + - [ ] Given decision D1 + - [ ] When mark_superseded(D1.id, D2.id) called + - [ ] Then D1.superseded_by equals D2.id + - [ ] Commit: "test(behave): add correction persistence scenarios" -**Parallel Group DOD: Definition of Done + Invariants [Luis + Jeff]** (depends on D2/D4) -- [ ] **COMMIT (Owner: Luis | Group: DOD.dod) - Commit message: "feat(dod): enforce definition-of-done gating"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Evaluate `definition_of_done` before apply; block apply with clear error if unmet. - - [ ] Code [Luis]: Ensure DoD templating uses plan arguments and preserves template in plan metadata. - - [ ] Docs [Luis]: Add `docs/reference/definition_of_done.md` with examples. - - [ ] Tests (Behave) [Luis]: Add DoD pass/fail scenarios. - - [ ] Tests (Robot) [Luis]: Add DoD integration smoke test. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/dod_evaluation_bench.py` for evaluation overhead. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(dod): enforce definition-of-done gating"`. -- [ ] **COMMIT (Owner: Jeff | Group: DOD.invariants) - Commit message: "feat(invariant): add invariant models and enforcement"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Add invariant models, merge order (plan > project > action > global), and enforcement before strategize. - - [ ] Code [Jeff]: Add Invariant Reconciliation Actor role and record `invariant_enforced` decisions. - - [ ] Code [Jeff]: Add `agents invariant add/list/remove` CLI with scope flags. - - [ ] Docs [Jeff]: Add `docs/reference/invariants.md` and update CLI reference. - - [ ] Tests (Behave) [Jeff]: Add invariant merge + violation scenarios. - - [ ] Tests (Robot) [Jeff]: Add invariant CLI integration tests. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/invariant_merge_bench.py` for merge overhead. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(invariant): add invariant models and enforcement"`. +**M4 SUCCESS CRITERIA** (Day 21): +- [ ] Decisions are recorded during Strategize phase with full context +- [ ] Decision tree can be viewed via `agents [--data-dir PATH] [--config-path PATH] plan tree` command +- [ ] `agents [--data-dir PATH] [--config-path PATH] plan explain ` shows full decision details +- [ ] Correction with `--mode=revert` rolls back and re-executes from decision point +- [ ] Correction with `--mode=append` creates fix subplan without modifying history +- [ ] Decisions persist to database and survive restart +- [ ] Context snapshots stored and retrievable for replay +--- ### Section 7: Subplans & Parallelism [M5] **Target: Milestone M5 (+25 days)** -**Week 3-4 focus**: subplan spawning, parallel execution, and result merging. -**Parallel Group E1: Subplan Domain [Luis + Rui]** -- [ ] **COMMIT (Owner: Luis | Group: E1.domain) - Commit message: "feat(domain): add subplan config and status models"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Add `ExecutionMode` enum (sequential, parallel, hybrid) with validation guards. - - [ ] Code [Luis]: Add `MergeStrategy` enum (three_way, sequential, json) with defaults. - - [ ] Code [Luis]: Add `SubplanConfig` model fields: parent_plan_id, spawn_decision_id, dependencies, max_parallel, merge_strategy, automation_profile_override, invariants_override, context_view_override. - - [ ] Code [Luis]: Add `SubplanStatus` model fields: subplan_id, state, started_at, completed_at, error_message, changeset_id. - - [ ] Code [Luis]: Add `SubplanAttempt` model fields: attempt_id (ULID), subplan_id, attempt_number, started_at, completed_at, error_details. - - [ ] Code [Luis]: Extend `Plan` with `subplan_config`, `subplan_statuses`, `spawn_decision_id`, and helpers (`is_subplan`, `has_subplans`, `child_count`). - - [ ] Code [Luis]: Add DecisionType constants for `subplan_spawn` and `subplan_parallel_spawn` and ensure models reference them. - - [ ] Docs [Luis]: Add `docs/reference/subplan_model.md`. - - [ ] Tests (Behave) [Luis]: Add `features/subplan_model.feature` scenarios for config validation, dependency cycles, and parent/root helpers. - - [ ] Tests (Robot) [Luis]: Add `robot/subplan_model.robot` smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/subplan_model_bench.py` for model validation. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(domain): add subplan config and status models"`. +**CRITICAL FOR 30-DAY GOAL**: Subplans enable large project handling (e.g., converting Firefox to Rust uses hierarchical decomposition into thousands of subplans) -**Parallel Group E2: Subplan Spawning [Jeff + Aditya]** (depends on D2 + E1) -- [ ] **COMMIT (Owner: Jeff | Group: E2.service) - Commit message: "feat(service): add subplan service and spawn workflow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement `SubplanService` with `spawn_subplan`, `spawn_batch`, tree queries, and bounded context builder. - - [ ] Code [Jeff]: Build bounded context from parent plan decisions + project context policies; enforce token/file limits. - - [ ] Code [Jeff]: Inherit automation profile + invariants from parent plan; allow subplan overrides from decisions. - - [ ] Code [Jeff]: Persist subplan config into child Plan metadata (`subplan_config`) and link `spawn_decision_id`. - - [ ] Code [Jeff]: Link SUBPLAN_SPAWN decisions to created subplans and status tracking. - - [ ] Docs [Jeff]: Add `docs/reference/subplan_service.md`. - - [ ] Tests (Behave) [Jeff]: Add subplan spawn scenarios (inheritance, overrides, dependency ordering). - - [ ] Tests (Robot) [Jeff]: Add subplan spawn integration tests. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/subplan_spawn_bench.py` for spawn throughput. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(service): add subplan service and spawn workflow"`. -- [ ] **COMMIT (Owner: Aditya | Group: E2.actor) - Commit message: "feat(actor): add plan_subplan tool and decision emission"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Aditya]: Add `plan_subplan` tool to strategy actors and emit SUBPLAN_SPAWN decisions. - - [ ] Code [Aditya]: Support `parallel=true` to emit SUBPLAN_PARALLEL_SPAWN and include dependency list. - - [ ] Code [Aditya]: Include merge strategy, resource scope, and context view overrides in decision payload. - - [ ] Docs [Aditya]: Update actor YAML examples for subplan emission. - - [ ] Tests (Behave) [Aditya]: Add scenarios for subplan decision emission (parallel + dependencies). - - [ ] Tests (Robot) [Aditya]: Add actor tool integration smoke tests. - - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/subplan_actor_tool_bench.py` for tool invocation overhead. - - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Aditya]: `git commit -m "feat(actor): add plan_subplan tool and decision emission"`. +- [ ] **Stage E1: Subplan Model** (Day 12) **[Luis]** + + **SEQUENTIAL ORDER**: E1.1 (Enums) → E1.2 (SubplanConfig) → E1.3 (Plan extension) → E1.4 (SubplanStatus) → E1.5 (Failure rules) → E1.6 (Tests) + + - [ ] **E1.1** [Luis] Define execution enums in `src/cleveragents/domain/models/core/plan.py`: + - [ ] **E1.1a** [Luis] Define `ExecutionMode` enum: + ```python + class ExecutionMode(str, Enum): + """How subplans should be executed.""" + SEQUENTIAL = "sequential" # One after another, ordered by sequence + PARALLEL = "parallel" # All at once (up to max_parallel) + DEPENDENCY_ORDERED = "dependency_ordered" # Respect DAG dependencies + ``` + - [ ] Commit: "feat(domain): define ExecutionMode enum" + - [ ] **E1.1b** [Luis] Define `MergeStrategy` enum: + ```python + class MergeStrategy(str, Enum): + """How to merge results from parallel subplans.""" + GIT_THREE_WAY = "git_three_way" # Use git merge-file for code + SEQUENTIAL_APPLY = "sequential_apply" # Apply in completion order + FAIL_ON_CONFLICT = "fail_on_conflict" # Error if any conflicts + LAST_WINS = "last_wins" # Later changes overwrite earlier + ``` + - [ ] Commit: "feat(domain): define MergeStrategy enum" + - [ ] **E1.2** [Luis] Define `SubplanConfig` model: + - [ ] **E1.2a** [Luis] Create SubplanConfig dataclass: + ```python + class SubplanConfig(BaseModel): + """Configuration for subplan execution.""" + + execution_mode: ExecutionMode = Field( + default=ExecutionMode.SEQUENTIAL, + description="How to execute subplans" + ) + merge_strategy: MergeStrategy = Field( + default=MergeStrategy.GIT_THREE_WAY, + description="How to merge subplan results" + ) + max_parallel: int = Field( + default=5, ge=1, le=50, + description="Max concurrent subplans (for PARALLEL mode)" + ) + fail_fast: bool = Field( + default=False, + description="Stop all subplans on first failure" + ) + timeout_per_subplan_seconds: int | None = Field( + default=None, + description="Timeout for each subplan (None=no timeout)" + ) + retry_failed: bool = Field( + default=True, + description="Automatically retry failed subplans" + ) + max_retries: int = Field( + default=2, ge=0, le=5, + description="Max retry attempts per subplan" + ) + ``` + - [ ] Commit: "feat(domain): define SubplanConfig model" + - [ ] **E1.3** [Luis] Extend Plan model for subplan hierarchy: + - [ ] **E1.3a** [Luis] Add parent/root plan fields (verify exist): + ```python + # In Plan model + parent_plan_id: str | None = Field( + default=None, + description="Parent plan ID if this is a subplan" + ) + root_plan_id: str | None = Field( + default=None, + description="Root plan ID (topmost ancestor)" + ) + ``` + - [ ] Commit: "feat(domain): verify parent/root plan fields on Plan" + - [ ] **E1.3b** [Luis] Add subplan configuration field: + ```python + subplan_config: SubplanConfig | None = Field( + default=None, + description="Config for subplan execution (set on parent plans)" + ) + subplan_statuses: list["SubplanStatus"] = Field( + default_factory=list, + description="Status tracking for spawned subplans" + ) + ``` + - [ ] Commit: "feat(domain): add subplan config and status fields to Plan" + - [ ] **E1.3c** [Luis] Add computed properties: + ```python + @property + def is_subplan(self) -> bool: + """Check if this plan is a subplan (has parent).""" + return self.parent_plan_id is not None + + @property + def is_root_plan(self) -> bool: + """Check if this is the root plan.""" + return self.root_plan_id is None or self.root_plan_id == self.plan_id + + @property + def depth(self) -> int: + """Distance from root plan (0 for root).""" + # Note: This requires parent chain traversal + # For efficiency, may be cached or stored + if self.is_root_plan: + return 0 + # Computed by service layer traversing parent_plan_id chain + return -1 # Placeholder, computed externally + + @property + def has_subplans(self) -> bool: + """Check if this plan has spawned subplans.""" + return len(self.subplan_statuses) > 0 + ``` + - [ ] Commit: "feat(domain): add subplan computed properties to Plan" + - [ ] **E1.4** [Luis] Define `SubplanStatus` tracking model: + - [ ] **E1.4a** [Luis] Create SubplanStatus dataclass: + ```python + @dataclass + class SubplanStatus: + """Track status of a spawned subplan.""" + + subplan_id: str # The subplan's plan_id + action_name: str # Action used to create subplan + target_resources: list[str] # Resources subplan works on + + # Status tracking + status: ProcessingState = ProcessingState.QUEUED + started_at: datetime | None = None + completed_at: datetime | None = None + + # Results + error: str | None = None + changeset_summary: str | None = None # Brief summary of changes + files_changed: int = 0 + + # Retries + attempt_number: int = 1 + previous_attempts: list["SubplanAttempt"] = field(default_factory=list) + ``` + - [ ] Commit: "feat(domain): define SubplanStatus dataclass" + - [ ] **E1.4b** [Luis] Define SubplanAttempt for retry tracking: + ```python + @dataclass + class SubplanAttempt: + """Record of a subplan execution attempt.""" + attempt_number: int + started_at: datetime + completed_at: datetime | None + error: str | None + was_retried: bool + ``` + - [ ] Commit: "feat(domain): define SubplanAttempt dataclass" + - [ ] **E1.5** [Luis] Define subplan failure handling rules: + - [ ] **E1.5a** [Luis] Create `SubplanFailureHandler` class: + ```python + class SubplanFailureHandler: + """Handle subplan failures based on configuration.""" + + def should_stop_others( + self, + config: SubplanConfig, + failed_status: SubplanStatus + ) -> bool: + """Determine if other subplans should stop.""" + if config.fail_fast: + return True + if config.execution_mode == ExecutionMode.SEQUENTIAL: + return True # Sequential always stops on failure + return False # Parallel continues others + + def should_retry( + self, + config: SubplanConfig, + status: SubplanStatus + ) -> bool: + """Determine if failed subplan should be retried.""" + if not config.retry_failed: + return False + if status.attempt_number > config.max_retries: + return False + # Don't retry on certain errors + if status.error and "ValidationError" in status.error: + return True # Validation failures can be retried + if status.error and "TimeoutError" in status.error: + return True # Timeouts can be retried + return False + ``` + - [ ] Commit: "feat(domain): define SubplanFailureHandler" + - [ ] **E1.5b** [Luis] Add failure state constants: + ```python + # Error = application/system bug, likely not recoverable + # Failure = task couldn't complete (tests fail, validation fail), may be retryable + + RETRIABLE_FAILURES = { + "ValidationError", + "TimeoutError", + "TemporaryResourceError", + "MergeConflictError" # May succeed with different merge strategy + } + + NON_RETRIABLE_ERRORS = { + "ConfigurationError", + "AuthenticationError", + "MissingResourceError", + "CircularDependencyError" + } + ``` + - [ ] Commit: "feat(domain): define retriable vs non-retriable failures" + - [ ] **E1.6** [Rui] Write Behave tests for subplan model: + - [ ] **E1.6a** [Rui] Plan hierarchy scenarios: + - [ ] Scenario: Plan with parent_plan_id has is_subplan=True + - [ ] Given Plan with parent_plan_id set + - [ ] Then is_subplan returns True + - [ ] And is_root_plan returns False + - [ ] Scenario: Root plan has is_subplan=False and is_root_plan=True + - [ ] Scenario: SubplanConfig validates max_parallel bounds + - [ ] Commit: "test(behave): add plan hierarchy scenarios" + - [ ] **E1.6b** [Rui] Execution mode scenarios: + - [ ] Scenario: ExecutionMode enum has all required values + - [ ] Scenario: MergeStrategy enum has all required values + - [ ] Scenario: SubplanConfig defaults are applied + - [ ] Commit: "test(behave): add execution mode scenarios" + - [ ] **E1.6c** [Rui] SubplanStatus scenarios: + - [ ] Scenario: SubplanStatus tracks state correctly + - [ ] Scenario: SubplanAttempt records retry history + - [ ] Scenario: Failure handler respects fail_fast setting + - [ ] Commit: "test(behave): add SubplanStatus scenarios" -**Parallel Group E3: Parallel Execution [Luis + Jeff]** (depends on E1/E2) -- [ ] **COMMIT (Owner: Luis | Group: E3.exec) - Commit message: "feat(service): add subplan scheduler and execution"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Add subplan scheduler with `max_parallel`, dependency ordering, and fail-fast handling. - - [ ] Code [Luis]: Support sequential and parallel execution modes based on SubplanConfig. - - [ ] Code [Luis]: Track status updates for subplans and propagate to parent plan (processing/complete/errored). - - [ ] Code [Luis]: Add cancellation propagation from parent to child subplans. - - [ ] Docs [Luis]: Add `docs/reference/subplan_execution.md`. - - [ ] Tests (Behave) [Luis]: Add parallel + dependency execution scenarios. - - [ ] Tests (Robot) [Luis]: Add parallel execution integration tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/subplan_scheduler_bench.py` for scheduler overhead. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(service): add subplan scheduler and execution"`. +- [ ] **Stage E2: Subplan Spawning** (Day 12-13) **[Jeff + Luis]** + + **SEQUENTIAL ORDER**: E2.1 (Service scaffold) → E2.2 (spawn_subplan) → E2.3 (spawn_batch) → E2.4 (queries) → E2.5 (strategy actor) → E2.6 (execute phase) → E2.7 (status tracking) → E2.8 (bounded context) → E2.9 (Tests) + + - [ ] **E2.1** [Jeff] Create `SubplanService` scaffold in `src/cleveragents/application/services/subplan_service.py`: + - [ ] **E2.1a** [Jeff] Define service class: + ```python + class SubplanService: + """Service for spawning and managing subplans.""" + + def __init__( + self, + plan_service: PlanLifecycleService, + decision_service: DecisionService, + plan_repo: LifecyclePlanRepository, + context_builder: ContextBuilder + ): + self._plan_service = plan_service + self._decision_service = decision_service + self._plan_repo = plan_repo + self._context_builder = context_builder + ``` + - [ ] Commit: "feat(service): add SubplanService scaffold" + - [ ] **E2.2** [Jeff] Implement `spawn_subplan()` method: + - [ ] **E2.2a** [Jeff] Core implementation: + ```python + def spawn_subplan( + self, + parent_plan: Plan, + decision: Decision, + action_name: str, + target_resources: list[str] | None = None, + arguments: dict | None = None + ) -> Plan: + """Spawn a single subplan from a parent plan.""" + # Validate parent is not already a deep subplan + if parent_plan.depth >= 10: # Max nesting depth + raise MaxSubplanDepthError(f"Cannot spawn subplan at depth {parent_plan.depth + 1}") + + # Create subplan via plan service + subplan = self._plan_service.use_action( + action_name=action_name, + project_ids=target_resources or parent_plan.project_ids, + arguments=arguments or {}, + parent_plan_id=parent_plan.plan_id, + root_plan_id=parent_plan.root_plan_id or parent_plan.plan_id, + automation_level=parent_plan.automation_level + ) + + # Link to decision + self._decision_service.add_downstream_plan(decision.decision_id, subplan.plan_id) + + # Create status tracking + status = SubplanStatus( + subplan_id=subplan.plan_id, + action_name=action_name, + target_resources=target_resources or [] + ) + + # Update parent with new subplan status + self._update_parent_subplan_status(parent_plan.plan_id, status) + + logger.info(f"Spawned subplan {subplan.plan_id} from parent {parent_plan.plan_id}") + return subplan + ``` + - [ ] Commit: "feat(service): implement spawn_subplan()" + - [ ] **E2.2b** [Jeff] Add bounded context calculation: + ```python + def _build_bounded_context( + self, + parent_plan: Plan, + decision: Decision, + target_resources: list[str] + ) -> BoundedContext: + """Build bounded context for subplan from decision scope.""" + return self._context_builder.build_from_decision( + parent_context=parent_plan.context, + decision=decision, + resource_filter=target_resources + ) + ``` + - [ ] Commit: "feat(service): add bounded context for subplans" + - [ ] **E2.3** [Jeff] Implement `spawn_batch()` method: + - [ ] **E2.3a** [Jeff] Batch spawning implementation: + ```python + def spawn_batch( + self, + parent_plan: Plan, + decisions: list[Decision], + execution_mode: ExecutionMode = ExecutionMode.PARALLEL + ) -> list[Plan]: + """Spawn multiple subplans at once.""" + subplans = [] + + for decision in decisions: + if decision.decision_type != DecisionType.SUBPLAN_SPAWN: + continue + + # Extract spawn parameters from decision + spawn_params = self._extract_spawn_params(decision) + + subplan = self.spawn_subplan( + parent_plan=parent_plan, + decision=decision, + action_name=spawn_params["action_name"], + target_resources=spawn_params.get("target_resources"), + arguments=spawn_params.get("arguments") + ) + subplans.append(subplan) + + # Update parent's execution mode + self._update_parent_execution_mode(parent_plan.plan_id, execution_mode) + + logger.info(f"Spawned {len(subplans)} subplans from parent {parent_plan.plan_id}") + return subplans + + def _extract_spawn_params(self, decision: Decision) -> dict: + """Extract spawn parameters from SUBPLAN_SPAWN decision.""" + # Parse chosen_option which contains action name and params + # Format: "action_name:arg1=val1,arg2=val2" + return { + "action_name": decision.chosen_option.split(":")[0], + "target_resources": decision.context_snapshot.relevant_resources, + "arguments": {} # Parsed from decision metadata + } + ``` + - [ ] Commit: "feat(service): implement spawn_batch()" + - [ ] **E2.4** [Jeff] Implement query methods: + - [ ] **E2.4a** [Jeff] Implement `get_subplans()`: + ```python + def get_subplans(self, parent_plan_id: str) -> list[Plan]: + """Get all direct child subplans.""" + return self._plan_repo.get_children(parent_plan_id) + + def get_subplan_statuses(self, parent_plan_id: str) -> list[SubplanStatus]: + """Get status tracking for all subplans.""" + parent = self._plan_repo.get_by_id(parent_plan_id) + return parent.subplan_statuses if parent else [] + ``` + - [ ] Commit: "feat(service): implement get_subplans()" + - [ ] **E2.4b** [Jeff] Implement `get_full_tree()`: + ```python + def get_full_tree(self, root_plan_id: str) -> PlanTree: + """Get full subplan tree from root.""" + plans = self._plan_repo.get_tree(root_plan_id) + return self._build_plan_tree(plans, root_plan_id) + + def _build_plan_tree(self, plans: list[Plan], root_id: str) -> PlanTree: + """Build tree structure from flat list.""" + by_parent: dict[str, list[Plan]] = {} + root = None + + for plan in plans: + if plan.plan_id == root_id: + root = plan + elif plan.parent_plan_id: + by_parent.setdefault(plan.parent_plan_id, []).append(plan) + + def build_node(plan: Plan) -> PlanTreeNode: + children = [build_node(c) for c in by_parent.get(plan.plan_id, [])] + return PlanTreeNode(plan=plan, children=children) + + return PlanTree(root=build_node(root), total_count=len(plans)) + ``` + - [ ] Commit: "feat(service): implement get_full_tree()" + - [ ] **E2.5** [Aditya] Configure strategy actor to emit subplan_spawn decisions: + - [ ] **E2.5a** [Aditya] Add plan_subplan tool to strategy actor: + ```yaml + # In strategy_actor.yaml + tools: + - name: plan_subplan + description: | + Decompose work into a subplan for parallel or sequential execution. + Use when work can be broken into independent pieces. + parameters: + - name: action_name + type: string + description: Action to use for subplan (e.g., "local/code-fix") + - name: description + type: string + description: What the subplan should accomplish + - name: target_files + type: array + description: Files this subplan should work on + - name: execution_mode + type: string + enum: [sequential, parallel, dependency_ordered] + description: How this relates to other subplans + - name: depends_on + type: array + description: IDs of subplans this depends on (for dependency_ordered) + code: | + result = context.create_subplan_decision( + action_name=input_data["action_name"], + description=input_data["description"], + target_files=input_data.get("target_files", []), + execution_mode=input_data.get("execution_mode", "parallel"), + depends_on=input_data.get("depends_on", []) + ) + ``` + - [ ] Commit: "feat(actor): add plan_subplan tool to strategy actor" + - [ ] **E2.5b** [Aditya] Implement `context.create_subplan_decision()`: + ```python + def create_subplan_decision( + self, + action_name: str, + description: str, + target_files: list[str], + execution_mode: str = "parallel", + depends_on: list[str] | None = None + ) -> str: + """Create a SUBPLAN_SPAWN decision (not actual plan yet).""" + decision = self._decision_service.record_decision( + plan_id=self.plan_id, + decision_type=DecisionType.SUBPLAN_SPAWN, + question=f"Should we create subplan for: {description}", + chosen_option=f"{action_name}:{','.join(target_files)}", + hot_context=self._get_context_for_files(target_files), + resources=target_files, + parent_decision_id=self._current_decision_id, + rationale=description + ) + + # Store metadata for Execute phase to process + self._pending_subplans.append({ + "decision_id": decision.decision_id, + "action_name": action_name, + "target_files": target_files, + "execution_mode": execution_mode, + "depends_on": depends_on or [] + }) + + return decision.decision_id + ``` + - [ ] Commit: "feat(context): implement create_subplan_decision()" + - [ ] **E2.6** [Jeff] Execute phase processes subplan decisions: + - [ ] **E2.6a** [Jeff] Add subplan processing to execute phase: + ```python + # In PlanLifecycleService.execute_execution() + + async def _process_subplan_decisions(self, plan: Plan) -> None: + """Process SUBPLAN_SPAWN decisions after strategy.""" + # Get all pending subplan decisions + decisions = self._decision_service.get_by_plan_and_type( + plan.plan_id, DecisionType.SUBPLAN_SPAWN + ) + + if not decisions: + return + + # Group by execution mode + parallel_decisions = [] + sequential_decisions = [] + dependency_decisions = [] + + for d in decisions: + mode = self._get_execution_mode(d) + if mode == ExecutionMode.PARALLEL: + parallel_decisions.append(d) + elif mode == ExecutionMode.SEQUENTIAL: + sequential_decisions.append(d) + else: + dependency_decisions.append(d) + + # Execute in appropriate order + if parallel_decisions: + await self._execute_parallel_subplans(plan, parallel_decisions) + if sequential_decisions: + await self._execute_sequential_subplans(plan, sequential_decisions) + if dependency_decisions: + await self._execute_dependency_ordered_subplans(plan, dependency_decisions) + ``` + - [ ] Commit: "feat(service): add subplan decision processing to execute phase" + - [ ] **E2.6b** [Jeff] Implement sequential execution: + ```python + async def _execute_sequential_subplans( + self, + parent: Plan, + decisions: list[Decision] + ) -> None: + """Execute subplans one at a time in order.""" + for decision in decisions: + subplan = self._subplan_service.spawn_subplan( + parent_plan=parent, + decision=decision, + action_name=self._extract_action_name(decision) + ) + + # Execute and wait + await self._execute_subplan(subplan) + + # Check result + status = self._get_subplan_status(parent, subplan.plan_id) + if status.status == ProcessingState.ERRORED: + if parent.subplan_config.fail_fast: + raise SubplanFailedError(subplan.plan_id, status.error) + # Otherwise continue to next + ``` + - [ ] Commit: "feat(service): implement sequential subplan execution" + - [ ] **E2.7** [Luis] Implement subplan status tracking: + - [ ] **E2.7a** [Luis] Create status update mechanism: + ```python + class SubplanStatusTracker: + """Track and update subplan statuses.""" + + def __init__(self, plan_repo: LifecyclePlanRepository): + self._plan_repo = plan_repo + self._listeners: dict[str, list[Callable]] = {} + + def update_status( + self, + parent_plan_id: str, + subplan_id: str, + new_status: ProcessingState, + error: str | None = None + ) -> None: + """Update status of a subplan.""" + parent = self._plan_repo.get_by_id(parent_plan_id) + if not parent: + return + + # Find and update status + for status in parent.subplan_statuses: + if status.subplan_id == subplan_id: + status.status = new_status + if new_status == ProcessingState.PROCESSING: + status.started_at = datetime.utcnow() + elif new_status in (ProcessingState.COMPLETE, ProcessingState.ERRORED): + status.completed_at = datetime.utcnow() + if error: + status.error = error + break + + # Persist + self._plan_repo.update(parent) + + # Notify listeners + self._notify_listeners(parent_plan_id, subplan_id, new_status) + + def subscribe(self, parent_plan_id: str, callback: Callable) -> None: + """Subscribe to status updates for a parent plan.""" + self._listeners.setdefault(parent_plan_id, []).append(callback) + ``` + - [ ] Commit: "feat(service): implement SubplanStatusTracker" + - [ ] **E2.7b** [Luis] Determine parent state from subplan states: + ```python + def compute_parent_state(self, parent: Plan) -> ProcessingState: + """Compute parent state based on subplan states.""" + statuses = parent.subplan_statuses + + if not statuses: + return parent.state + + # Count states + errored = sum(1 for s in statuses if s.status == ProcessingState.ERRORED) + complete = sum(1 for s in statuses if s.status == ProcessingState.COMPLETE) + processing = sum(1 for s in statuses if s.status == ProcessingState.PROCESSING) + + config = parent.subplan_config or SubplanConfig() + + # Determine parent state + if processing > 0: + return ProcessingState.PROCESSING + + if errored > 0: + if config.execution_mode == ExecutionMode.PARALLEL: + # Parallel: error only if ALL failed + if errored == len(statuses): + return ProcessingState.ERRORED + else: + # Sequential: error on first failure + return ProcessingState.ERRORED + + if complete == len(statuses): + return ProcessingState.COMPLETE + + return ProcessingState.QUEUED # Some still pending + ``` + - [ ] Commit: "feat(service): implement parent state computation" + - [ ] **E2.8** [Luis] Implement bounded context for subplans: + - [ ] **E2.8a** [Luis] Create `ContextBuilder` for bounded contexts: + ```python + class ContextBuilder: + """Build bounded contexts for subplans.""" + + def build_from_decision( + self, + parent_context: PlanContext, + decision: Decision, + resource_filter: list[str] + ) -> BoundedContext: + """Build context bounded to decision scope.""" + # Filter files to only those relevant + relevant_files = self._filter_files( + parent_context.files, + resource_filter + ) + + # Include decision chain for reference + decision_chain = self._get_decision_chain(decision) + + return BoundedContext( + files=relevant_files, + decision_chain=decision_chain, + parent_context_ref=parent_context.context_id, + boundary=resource_filter + ) + + def _filter_files( + self, + files: dict[str, FileContent], + patterns: list[str] + ) -> dict[str, FileContent]: + """Filter files to match patterns.""" + import fnmatch + result = {} + for path, content in files.items(): + if any(fnmatch.fnmatch(path, p) for p in patterns): + result[path] = content + return result + ``` + - [ ] Commit: "feat(context): implement ContextBuilder for bounded contexts" + - [ ] **E2.9** [Rui] Write integration tests for subplan spawning: + - [ ] **E2.9a** [Rui] Spawn scenarios: + - [ ] Scenario: SUBPLAN_SPAWN decision creates child plan + - [ ] Given strategy produces SUBPLAN_SPAWN decision + - [ ] When execute phase processes decisions + - [ ] Then child plan is created with correct parent_plan_id + - [ ] And decision.downstream_plan_ids contains subplan ID + - [ ] Scenario: spawn_batch creates multiple subplans + - [ ] Given 3 SUBPLAN_SPAWN decisions + - [ ] When spawn_batch is called + - [ ] Then 3 subplans are created + - [ ] Commit: "test(behave): add subplan spawn scenarios" + - [ ] **E2.9b** [Rui] Execution order scenarios: + - [ ] Scenario: Sequential subplans execute in order + - [ ] Given 3 sequential subplans S1, S2, S3 + - [ ] When executed + - [ ] Then S1 completes before S2 starts + - [ ] And S2 completes before S3 starts + - [ ] Scenario: Parallel subplans execute concurrently + - [ ] Given 3 parallel subplans + - [ ] When executed with max_parallel=3 + - [ ] Then all 3 start at approximately same time + - [ ] Commit: "test(behave): add execution order scenarios" + - [ ] **E2.9c** [Rui] Failure scenarios: + - [ ] Scenario: Failed sequential subplan stops processing + - [ ] Given sequential subplans S1, S2, S3 + - [ ] When S2 fails + - [ ] Then S3 is not started + - [ ] And parent enters ERRORED state + - [ ] Scenario: Failed parallel subplan allows others to finish + - [ ] Given parallel subplans S1, S2, S3 + - [ ] And fail_fast=False + - [ ] When S2 fails + - [ ] Then S1 and S3 continue to completion + - [ ] Commit: "test(behave): add subplan failure scenarios" -**Parallel Group E4: Result Merging [Jeff]** (depends on E3) -- [ ] **COMMIT (Owner: Jeff | Group: E4.merge) - Commit message: "feat(merge): add subplan merge strategies"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Add three-way merge strategy for file changes and conflict markers. - - [ ] Code [Jeff]: Add sequential merge and JSON merge strategies; expose merge result artifacts. - - [ ] Code [Jeff]: Add conflict artifact model (file_path, conflict_type, base/left/right snippets). - - [ ] Code [Jeff]: Store merge output as ChangeSet and attach to parent plan for review. - - [ ] Docs [Jeff]: Add `docs/reference/subplan_merge.md`. - - [ ] Tests (Behave) [Jeff]: Add merge + conflict scenarios. - - [ ] Tests (Robot) [Jeff]: Add merge integration tests for multi-subplan plans. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/subplan_merge_bench.py` for merge performance. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(merge): add subplan merge strategies"`. +- [ ] **Stage E3: Parallel Execution** (Day 19) **[Jeff + Luis]** + + **SEQUENTIAL ORDER**: E3.1 (AsyncExecutor) → E3.2 (Semaphore) → E3.3 (Timeouts) → E3.4 (DAG) → E3.5 (Isolation) → E3.6 (Tests) + + - [ ] **E3.1** [Jeff] Implement async subplan executor: + - [ ] **E3.1a** [Jeff] Create `AsyncSubplanExecutor` class: + ```python + class AsyncSubplanExecutor: + """Execute subplans asynchronously with concurrency control.""" + + def __init__( + self, + plan_service: PlanLifecycleService, + status_tracker: SubplanStatusTracker + ): + self._plan_service = plan_service + self._status_tracker = status_tracker + + async def execute_parallel( + self, + parent: Plan, + subplans: list[Plan], + config: SubplanConfig + ) -> list[SubplanResult]: + """Execute subplans in parallel with concurrency limit.""" + semaphore = asyncio.Semaphore(config.max_parallel) + + async def execute_with_limit(subplan: Plan) -> SubplanResult: + async with semaphore: + return await self._execute_single(subplan, config) + + tasks = [execute_with_limit(sp) for sp in subplans] + results = await asyncio.gather(*tasks, return_exceptions=True) + + return self._process_results(results, subplans) + ``` + - [ ] Commit: "feat(executor): add AsyncSubplanExecutor" + - [ ] **E3.1b** [Jeff] Implement single subplan execution: + ```python + async def _execute_single( + self, + subplan: Plan, + config: SubplanConfig + ) -> SubplanResult: + """Execute a single subplan with timeout.""" + try: + # Apply timeout if configured + if config.timeout_per_subplan_seconds: + result = await asyncio.wait_for( + self._run_subplan(subplan), + timeout=config.timeout_per_subplan_seconds + ) + else: + result = await self._run_subplan(subplan) + + return SubplanResult( + subplan_id=subplan.plan_id, + success=True, + changeset=result.changeset + ) + + except asyncio.TimeoutError: + self._status_tracker.update_status( + subplan.parent_plan_id, + subplan.plan_id, + ProcessingState.ERRORED, + error="Timeout exceeded" + ) + return SubplanResult( + subplan_id=subplan.plan_id, + success=False, + error="TimeoutError" + ) + + except Exception as e: + self._status_tracker.update_status( + subplan.parent_plan_id, + subplan.plan_id, + ProcessingState.ERRORED, + error=str(e) + ) + return SubplanResult( + subplan_id=subplan.plan_id, + success=False, + error=str(e) + ) + ``` + - [ ] Commit: "feat(executor): implement single subplan execution with timeout" + - [ ] **E3.2** [Jeff] Implement dependency-ordered execution: + - [ ] **E3.2a** [Jeff] Build and validate dependency DAG: + ```python + def build_dependency_dag( + self, + decisions: list[Decision] + ) -> DependencyGraph: + """Build DAG from subplan decisions with depends_on.""" + graph = DependencyGraph() + + for decision in decisions: + graph.add_node(decision.decision_id) + depends_on = self._get_depends_on(decision) + for dep_id in depends_on: + graph.add_edge(dep_id, decision.decision_id) + + # Validate no cycles + if graph.has_cycle(): + cycle = graph.find_cycle() + raise CircularDependencyError(f"Cycle detected: {' -> '.join(cycle)}") + + return graph + ``` + - [ ] Commit: "feat(executor): implement dependency DAG building" + - [ ] **E3.2b** [Jeff] Execute in topological order: + ```python + async def execute_dependency_ordered( + self, + parent: Plan, + decisions: list[Decision], + config: SubplanConfig + ) -> list[SubplanResult]: + """Execute subplans respecting dependency order.""" + dag = self.build_dependency_dag(decisions) + execution_order = dag.topological_sort() + + results = [] + completed: set[str] = set() + + # Process in waves - each wave contains independent nodes + while execution_order: + # Find all nodes whose dependencies are satisfied + ready = [ + node for node in execution_order + if all(dep in completed for dep in dag.get_dependencies(node)) + ] + + if not ready: + break # Stuck - shouldn't happen with valid DAG + + # Execute ready nodes in parallel + ready_decisions = [d for d in decisions if d.decision_id in ready] + subplans = [self._spawn_subplan(parent, d) for d in ready_decisions] + + wave_results = await self.execute_parallel(parent, subplans, config) + results.extend(wave_results) + + # Mark completed + for r in wave_results: + if r.success: + completed.add(r.decision_id) + + # Remove from order + execution_order = [n for n in execution_order if n not in ready] + + return results + ``` + - [ ] Commit: "feat(executor): implement dependency-ordered execution" + - [ ] **E3.3** [Luis] Implement subplan isolation: + - [ ] **E3.3a** [Luis] Ensure separate sandboxes: + ```python + def ensure_isolated_sandbox( + self, + subplan: Plan, + resource_service: ResourceService + ) -> None: + """Ensure subplan has its own isolated sandbox.""" + for resource_id in subplan.project_ids: + resource = self._get_resource(resource_id) + # Each subplan gets unique sandbox for same resource + sandbox = resource_service.access_resource( + plan_id=subplan.plan_id, # Use subplan ID, not parent + resource=resource, + mode=AccessMode.WRITE + ) + # Sandbox is isolated by plan_id + ``` + - [ ] Commit: "feat(executor): ensure isolated sandboxes for subplans" + - [ ] **E3.3b** [Luis] Prevent cross-subplan visibility: + ```python + def validate_isolation( + self, + subplan: Plan, + other_subplans: list[Plan] + ) -> None: + """Verify subplan cannot access other subplans' state.""" + subplan_sandbox = self._get_sandbox(subplan.plan_id) + + for other in other_subplans: + if other.plan_id == subplan.plan_id: + continue + other_sandbox = self._get_sandbox(other.plan_id) + + # Verify different paths + if subplan_sandbox.sandbox_path == other_sandbox.sandbox_path: + raise IsolationViolationError( + f"Subplans {subplan.plan_id} and {other.plan_id} share sandbox" + ) + ``` + - [ ] Commit: "feat(executor): add isolation validation" + - [ ] **E3.4** [Rui] Write tests for parallel execution: + - [ ] **E3.4a** [Rui] Concurrency scenarios: + - [ ] Scenario: 10 independent subplans run with max_parallel=5 + - [ ] Given 10 subplans with no dependencies + - [ ] And max_parallel=5 + - [ ] When executed in parallel + - [ ] Then at most 5 run concurrently at any time + - [ ] And all 10 complete successfully + - [ ] Commit: "test(behave): add concurrency limit scenarios" + - [ ] **E3.4b** [Rui] Dependency scenarios: + - [ ] Scenario: Dependency chain executes in correct order + - [ ] Given subplans A -> B -> C (B depends on A, C depends on B) + - [ ] When executed with dependency ordering + - [ ] Then A completes before B starts + - [ ] And B completes before C starts + - [ ] Scenario: Diamond dependency executes correctly + - [ ] Given A -> B, A -> C, B -> D, C -> D + - [ ] When executed + - [ ] Then A runs first + - [ ] Then B and C run in parallel + - [ ] Then D runs last + - [ ] Commit: "test(behave): add dependency ordering scenarios" + - [ ] **E3.4c** [Rui] Timeout scenarios: + - [ ] Scenario: Subplan timeout triggers failure + - [ ] Given subplan with timeout_per_subplan_seconds=10 + - [ ] When subplan takes 15 seconds + - [ ] Then subplan is marked ERRORED with TimeoutError + - [ ] Commit: "test(behave): add timeout scenarios" -**Parallel Group E5: Multi-Project Plans [Hamza]** (depends on E2/E4) -- [ ] **COMMIT (Owner: Hamza | Group: E5.multi) - Commit message: "feat(plan): add multi-project subplan support"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Allow plans to target multiple projects with separate resource link contexts. - - [ ] Code [Hamza]: Ensure sandbox isolation and cross-project dependency resolution. - - [ ] Code [Hamza]: Add plan metadata to track project-specific ChangeSets and validation summaries. - - [ ] Docs [Hamza]: Add `docs/reference/multi_project_plans.md`. - - [ ] Tests (Behave) [Hamza]: Add multi-project subplan scenarios. - - [ ] Tests (Robot) [Hamza]: Add multi-project integration tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/multi_project_bench.py` for multi-project overhead. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(plan): add multi-project subplan support"`. +- [ ] **Stage E4: Result Merging** (Day 20) **[Jeff + Luis]** + + **SEQUENTIAL ORDER**: E4.1 (MergeService) → E4.2 (MergeResult) → E4.3 (ThreeWayMerge) → E4.4 (SequentialMerge) → E4.5 (Validation) → E4.6 (Tests) + + - [ ] **E4.1** [Jeff] Create `MergeService` in `src/cleveragents/application/services/merge_service.py`: + - [ ] **E4.1a** [Jeff] Define service class: + ```python + class MergeService: + """Service for merging subplan results.""" + + def __init__( + self, + sandbox_manager: SandboxManager, + validation_service: ValidationService + ): + self._sandbox_manager = sandbox_manager + self._validation_service = validation_service + ``` + - [ ] Commit: "feat(service): add MergeService scaffold" + - [ ] **E4.1b** [Jeff] Implement `merge_subplan_results()`: + ```python + def merge_subplan_results( + self, + parent: Plan, + subplans: list[Plan], + strategy: MergeStrategy = MergeStrategy.GIT_THREE_WAY + ) -> MergeResult: + """Merge changesets from all completed subplans.""" + # Collect changesets + changesets = [sp.changeset for sp in subplans if sp.changeset] + + # Group changes by file + changes_by_file: dict[str, list[Change]] = {} + for cs in changesets: + for change in cs.changes: + changes_by_file.setdefault(change.path, []).append(change) + + # Detect and handle conflicts + merged_changes = [] + conflicts = [] + + for path, changes in changes_by_file.items(): + if len(changes) == 1: + merged_changes.append(changes[0]) + else: + # Multiple subplans modified same file + result = self._merge_file_changes(path, changes, strategy) + if result.has_conflict: + conflicts.append(result) + merged_changes.append(result.merged_change) + + return MergeResult( + merged_changeset=ChangeSet(changes=merged_changes), + conflicts=conflicts, + source_subplan_ids=[sp.plan_id for sp in subplans] + ) + ``` + - [ ] Commit: "feat(service): implement merge_subplan_results()" + - [ ] **E4.2** [Jeff] Define merge result types: + - [ ] **E4.2a** [Jeff] Define MergeResult dataclass: + ```python + @dataclass + class MergeResult: + """Result of merging subplan changesets.""" + merged_changeset: ChangeSet + conflicts: list["FileConflict"] + source_subplan_ids: list[str] + + @property + def has_conflicts(self) -> bool: + return len(self.conflicts) > 0 + + @property + def conflict_count(self) -> int: + return len(self.conflicts) + + @dataclass + class FileConflict: + """Conflict in a single file.""" + path: str + conflict_regions: list["ConflictRegion"] + subplan_ids: list[str] # Which subplans caused conflict + merged_content_with_markers: str + + @dataclass + class ConflictRegion: + """Region of conflict within a file.""" + start_line: int + end_line: int + ours_content: str # From first subplan + theirs_content: str # From second subplan + ``` + - [ ] Commit: "feat(domain): define merge result types" + - [ ] **E4.3** [Jeff] Implement git-style three-way merge: + - [ ] **E4.3a** [Jeff] Implement `_merge_file_changes()`: + ```python + def _merge_file_changes( + self, + path: str, + changes: list[Change], + strategy: MergeStrategy + ) -> FileMergeResult: + """Merge multiple changes to same file.""" + if strategy == MergeStrategy.GIT_THREE_WAY: + return self._git_three_way_merge(path, changes) + elif strategy == MergeStrategy.SEQUENTIAL_APPLY: + return self._sequential_merge(path, changes) + elif strategy == MergeStrategy.LAST_WINS: + return FileMergeResult( + merged_change=changes[-1], + has_conflict=False + ) + else: + raise ValueError(f"Unknown strategy: {strategy}") + ``` + - [ ] Commit: "feat(service): implement merge strategy dispatch" + - [ ] **E4.3b** [Jeff] Implement `_git_three_way_merge()`: + ```python + def _git_three_way_merge( + self, + path: str, + changes: list[Change] + ) -> FileMergeResult: + """Perform git-style three-way merge.""" + import subprocess + import tempfile + + # Get base (original) content + base_content = self._get_base_content(path) + + # For now, handle 2 changes; extend for more + if len(changes) != 2: + # Fall back to sequential for >2 changes + return self._sequential_merge(path, changes) + + ours = changes[0].content or base_content + theirs = changes[1].content or base_content + + # Write to temp files + with tempfile.NamedTemporaryFile(mode='w', suffix='.base', delete=False) as f: + f.write(base_content) + base_path = f.name + with tempfile.NamedTemporaryFile(mode='w', suffix='.ours', delete=False) as f: + f.write(ours) + ours_path = f.name + with tempfile.NamedTemporaryFile(mode='w', suffix='.theirs', delete=False) as f: + f.write(theirs) + theirs_path = f.name + + try: + # Run git merge-file + result = subprocess.run( + ['git', 'merge-file', '-p', ours_path, base_path, theirs_path], + capture_output=True, + text=True + ) + + merged_content = result.stdout + has_conflict = result.returncode != 0 + + # Parse conflict markers if present + conflicts = [] + if has_conflict: + conflicts = self._parse_conflict_markers(merged_content) + + merged_change = Change( + path=path, + operation=OperationType.MODIFY, + content=merged_content + ) + + return FileMergeResult( + merged_change=merged_change, + has_conflict=has_conflict, + conflict_regions=conflicts + ) + finally: + # Cleanup temp files + for p in [base_path, ours_path, theirs_path]: + os.unlink(p) + ``` + - [ ] Commit: "feat(service): implement git three-way merge" + - [ ] **E4.4** [Luis] Implement sequential merge: + - [ ] **E4.4a** [Luis] Apply changes in order: + ```python + def _sequential_merge( + self, + path: str, + changes: list[Change] + ) -> FileMergeResult: + """Apply changes sequentially in completion order.""" + current_content = self._get_base_content(path) + + for change in changes: + if change.edits: + # Apply edits + current_content = self._apply_edits(current_content, change.edits) + elif change.content: + # Full replacement + current_content = change.content + + return FileMergeResult( + merged_change=Change( + path=path, + operation=OperationType.MODIFY, + content=current_content + ), + has_conflict=False + ) + ``` + - [ ] Commit: "feat(service): implement sequential merge" + - [ ] **E4.5** [Luis] Implement post-merge validation: + - [ ] **E4.5a** [Luis] Validate merged state: + ```python + async def validate_merged_result( + self, + parent: Plan, + merge_result: MergeResult + ) -> ValidationResult: + """Run validation on merged changes.""" + # Apply merged changes to temporary sandbox + temp_sandbox = self._sandbox_manager.create_temp_sandbox( + parent.plan_id, suffix="_merge_validation" + ) + + try: + # Apply merged changes + for change in merge_result.merged_changeset.changes: + self._apply_change_to_sandbox(temp_sandbox, change) + + # Run validation + result = await self._validation_service.validate_changeset( + merge_result.merged_changeset, + parent.project + ) + + if not result.passed: + logger.warning( + f"Post-merge validation failed: {result.errors}" + ) + + return result + finally: + temp_sandbox.cleanup() + ``` + - [ ] Commit: "feat(service): implement post-merge validation" + - [ ] **E4.5b** [Luis] Handle validation failures: + ```python + async def handle_validation_failure( + self, + parent: Plan, + merge_result: MergeResult, + validation_result: ValidationResult + ) -> MergeRecoveryAction: + """Determine recovery action for failed validation.""" + # Options: + # 1. Retry with different merge strategy + # 2. Escalate to user + # 3. Fall back to sequential execution + + if parent.subplan_config.merge_strategy == MergeStrategy.GIT_THREE_WAY: + # Try sequential as fallback + return MergeRecoveryAction.RETRY_SEQUENTIAL + + # Escalate to user + return MergeRecoveryAction.ESCALATE_TO_USER + ``` + - [ ] Commit: "feat(service): implement validation failure handling" + - [ ] **E4.6** [Rui] Write tests for result merging: + - [ ] **E4.6a** [Rui] Clean merge scenarios: + - [ ] Scenario: Two subplans modifying different files merge cleanly + - [ ] Given subplan A modifies file1.py + - [ ] And subplan B modifies file2.py + - [ ] When merged + - [ ] Then both changes are in merged_changeset + - [ ] And has_conflicts is False + - [ ] Scenario: Same file different lines merges cleanly + - [ ] Given subplan A changes line 10 of file.py + - [ ] And subplan B changes line 50 of file.py + - [ ] When merged with GIT_THREE_WAY + - [ ] Then both changes are preserved + - [ ] And has_conflicts is False + - [ ] Commit: "test(behave): add clean merge scenarios" + - [ ] **E4.6b** [Rui] Conflict scenarios: + - [ ] Scenario: Same lines creates conflict markers + - [ ] Given subplan A changes line 10 to "version A" + - [ ] And subplan B changes line 10 to "version B" + - [ ] When merged with GIT_THREE_WAY + - [ ] Then has_conflicts is True + - [ ] And merged content contains conflict markers + - [ ] Scenario: LAST_WINS strategy has no conflicts + - [ ] Given conflicting changes + - [ ] When merged with LAST_WINS + - [ ] Then has_conflicts is False + - [ ] And later change overwrites earlier + - [ ] Commit: "test(behave): add conflict merge scenarios" + - [ ] **E4.6c** [Rui] Validation scenarios: + - [ ] Scenario: Post-merge validation catches broken code + - [ ] Given merged code with syntax error + - [ ] When post-merge validation runs + - [ ] Then validation fails + - [ ] And recovery action is suggested + - [ ] Commit: "test(behave): add post-merge validation scenarios" +- [ ] **Stage E5: Multi-Project Plans** (Day 25) **[Hamza]** + + **SEQUENTIAL ORDER**: E5.1 (Model extension) → E5.2 (Strategy changes) → E5.3 (Apply per project) → E5.4 (Cross-project deps) → E5.5 (Tests) + + - [ ] **E5.1** [Hamza] Extend Plan model for multiple projects: + - [ ] **E5.1a** [Hamza] Add projects field to Plan: + ```python + # In Plan model + project_ids: list[str] = Field( + default_factory=list, + description="Project IDs this plan targets" + ) + + @property + def is_multi_project(self) -> bool: + """Check if plan targets multiple projects.""" + return len(self.project_ids) > 1 + ``` + - [ ] Commit: "feat(domain): add multi-project support to Plan" + - [ ] **E5.1b** [Hamza] Add per-project sandbox tracking: + ```python + project_sandboxes: dict[str, str] = Field( + default_factory=dict, + description="Map of project_id to sandbox_id" + ) + + project_apply_status: dict[str, ApplyStatus] = Field( + default_factory=dict, + description="Apply status per project" + ) + ``` + - [ ] Commit: "feat(domain): add per-project sandbox tracking" + - [ ] **E5.2** [Hamza] Update strategy to include project assignments: + - [ ] **E5.2a** [Hamza] Add project field to subplan decisions: + ```python + # Strategy actor can specify project for subplan + context.create_subplan_decision( + action_name="local/code-fix", + description="Fix auth in backend", + target_files=["src/auth/*.py"], + target_project="backend" # New field + ) + ``` + - [ ] Commit: "feat(actor): add project targeting to subplan decisions" + - [ ] **E5.2b** [Hamza] Build cross-project dependency graph: + ```python + def build_cross_project_dag( + self, + decisions: list[Decision] + ) -> CrossProjectDAG: + """Build dependency graph that spans projects.""" + dag = CrossProjectDAG() + + for decision in decisions: + project = self._get_target_project(decision) + dag.add_node(decision.decision_id, project=project) + + for dep_id in self._get_depends_on(decision): + dep_project = self._get_project_for_decision(dep_id) + dag.add_edge(dep_id, decision.decision_id) + + # Track cross-project edges + if dep_project != project: + dag.mark_cross_project_edge(dep_id, decision.decision_id) + + return dag + ``` + - [ ] Commit: "feat(service): implement cross-project dependency tracking" + - [ ] **E5.3** [Hamza] Apply commits per project: + - [ ] **E5.3a** [Hamza] Implement per-project apply: + ```python + async def apply_multi_project( + self, + plan: Plan + ) -> MultiProjectApplyResult: + """Apply changes to each project separately.""" + results = {} + + for project_id in plan.project_ids: + try: + result = await self._apply_single_project(plan, project_id) + results[project_id] = ApplyStatus.SUCCESS + except Exception as e: + results[project_id] = ApplyStatus.FAILED + logger.error(f"Apply failed for project {project_id}: {e}") + + # Don't fail others unless they depend on this one + if self._has_dependents(project_id, plan): + logger.warning(f"Dependents of {project_id} will also fail") + + return MultiProjectApplyResult( + project_statuses=results, + fully_applied=all(s == ApplyStatus.SUCCESS for s in results.values()) + ) + ``` + - [ ] Commit: "feat(service): implement per-project apply" + - [ ] **E5.3b** [Hamza] Support partial apply: + ```python + async def apply_partial( + self, + plan: Plan, + project_ids: list[str] + ) -> MultiProjectApplyResult: + """Apply only to specified projects.""" + # Validate selected projects are valid + invalid = set(project_ids) - set(plan.project_ids) + if invalid: + raise InvalidProjectError(f"Projects not in plan: {invalid}") + + # Check dependency constraints + for pid in project_ids: + deps = self._get_project_dependencies(plan, pid) + missing_deps = deps - set(project_ids) + if missing_deps: + raise DependencyNotAppliedError( + f"Project {pid} depends on unapplied: {missing_deps}" + ) + + return await self._apply_projects(plan, project_ids) + ``` + - [ ] Commit: "feat(service): implement partial project apply" + - [ ] **E5.4** [Hamza] Handle cross-project dependencies: + - [ ] **E5.4a** [Hamza] Enforce dependency order in apply: + ```python + def get_project_apply_order( + self, + plan: Plan + ) -> list[str]: + """Get order to apply projects respecting dependencies.""" + dag = self._build_project_dependency_dag(plan) + return dag.topological_sort() + + async def apply_in_dependency_order( + self, + plan: Plan + ) -> MultiProjectApplyResult: + """Apply projects in correct dependency order.""" + order = self.get_project_apply_order(plan) + results = {} + + for project_id in order: + # Check all dependencies succeeded + deps = self._get_project_dependencies(plan, project_id) + if not all(results.get(d) == ApplyStatus.SUCCESS for d in deps): + results[project_id] = ApplyStatus.SKIPPED_DEPENDENCY_FAILED + continue + + # Apply this project + result = await self._apply_single_project(plan, project_id) + results[project_id] = result + + return MultiProjectApplyResult(project_statuses=results) + ``` + - [ ] Commit: "feat(service): implement dependency-ordered project apply" + - [ ] **E5.5** [Rui] Write end-to-end tests for multi-project: + - [ ] **E5.5a** [Rui] Multi-project scenarios: + - [ ] Scenario: Plan targets two projects with separate sandboxes + - [ ] Given plan with project_ids=[proj1, proj2] + - [ ] When plan executes + - [ ] Then each project has separate sandbox + - [ ] Scenario: Changes applied to each project separately + - [ ] Given completed multi-project plan + - [ ] When apply is called + - [ ] Then proj1 gets its changes + - [ ] And proj2 gets its changes + - [ ] Commit: "test(behave): add multi-project scenarios" + - [ ] **E5.5b** [Rui] Cross-project dependency scenarios: + - [ ] Scenario: Dependent project waits for dependency + - [ ] Given proj2 depends on proj1 changes + - [ ] When apply runs + - [ ] Then proj1 is applied first + - [ ] And proj2 is applied after proj1 succeeds + - [ ] Scenario: Dependent project skipped if dependency fails + - [ ] Given proj2 depends on proj1 + - [ ] And proj1 apply fails + - [ ] Then proj2 is marked SKIPPED_DEPENDENCY_FAILED + - [ ] Commit: "test(behave): add cross-project dependency scenarios" -### Section 8: Large Project Autonomy & Context [M6] +**M5 SUCCESS CRITERIA** (Day 25): +- [ ] Plans can spawn subplans from SUBPLAN_SPAWN decisions +- [ ] Sequential subplan execution works (execute in order) +- [ ] Parallel subplan execution works (concurrent with max_parallel limit) +- [ ] Dependency-ordered execution respects DAG +- [ ] Results from multiple subplans merge correctly using three-way merge +- [ ] Merge conflicts are marked and can be resolved +- [ ] Plans can target multiple projects with separate sandboxes +- [ ] Cross-project dependencies handled correctly +- [ ] Large task (10+ subplans) completes successfully -**Target: Milestone M6 (+30 days)** -**Local-mode only**: large-project autonomy is required; server connectivity remains stubbed. +--- -**Parallel Group G1: Large-Project Decomposition [Jeff]** -- [ ] **COMMIT (Owner: Jeff | Group: G1.decompose) - Commit message: "feat(plan): add large-project decomposition and dependency closure"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Add hierarchical decomposition with 4+ levels and bounded context per subplan. - - [ ] Code [Jeff]: Implement decomposition heuristics (max_files_per_subplan, max_tokens_per_subplan, language/dir clustering). - - [ ] Code [Jeff]: Add dependency closure computation for large graphs and DAG execution ordering. - - [ ] Code [Jeff]: Add bounded dependency closure with cutoff thresholds and memoization for 10K+ files. - - [ ] Code [Jeff]: Record decomposition decisions in DecisionService (strategy_choice + subplan_spawn entries). - - [ ] Docs [Jeff]: Add `docs/reference/large_project_decomposition.md`. - - [ ] Tests (Behave) [Jeff]: Add deep hierarchy + dependency closure scenarios. - - [ ] Tests (Robot) [Jeff]: Add large-project decomposition integration tests. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/large_project_decompose_bench.py` for decomposition runtime. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(plan): add large-project decomposition and dependency closure"`. +### Section 8: Server Connectivity [DEFERRED - Beyond Day 30] -**Parallel Group G2: Checkpointing & Rollback [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: G2.checkpoint) - Commit message: "feat(checkpoint): add checkpointing and rollback"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Add checkpoint declarations for tools and plan-level rollback policy. - - [ ] Code [Luis]: Add `checkpoints` table (checkpoint_id ULID, plan_id, sandbox_ref, created_at, metadata_json). - - [ ] Code [Luis]: Implement `plan rollback ` command. - - [ ] Code [Luis]: Implement git-worktree checkpoint snapshots (commit hash or patch) and rollback restore. - - [ ] Docs [Luis]: Add `docs/reference/checkpointing.md`. - - [ ] Tests (Behave) [Luis]: Add checkpoint/rollback scenarios. - - [ ] Tests (Robot) [Luis]: Add rollback integration tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/checkpoint_rollback_bench.py` for rollback latency. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(checkpoint): add checkpointing and rollback"`. +**Target: Post-30-day work (NOT part of initial 30-day timeline)** -**Parallel Group G3: Semantic Validation [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: G3.semantic) - Commit message: "feat(validation): add semantic validation service"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Add semantic validation hooks during strategize/execute and error-pattern checks. - - [ ] Code [Luis]: Add rule registry for semantic validators (dependency cycles, API misuse, missing symbols). - - [ ] Code [Luis]: Integrate semantic validation results into ValidationPipeline as informational by default. - - [ ] Docs [Luis]: Add `docs/reference/semantic_validation.md`. - - [ ] Tests (Behave) [Luis]: Add semantic validation scenarios. - - [ ] Tests (Robot) [Luis]: Add semantic validation integration tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/semantic_validation_bench.py` for validation cost. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(validation): add semantic validation service"`. +> **IMPORTANT**: This section covers **client-side interfaces for connecting to an external server**. The server itself is a **separate project** that will be developed independently. This client will NOT include server functionality—it operates purely as a client that can either run in stand-alone local-only mode or connect to a separately deployed CleverAgents server. -**Parallel Group G4: Context Tiers & Views [Hamza + Rui]** -- [ ] **COMMIT (Owner: Hamza | Group: G4.context) - Commit message: "feat(context): add hot/warm/cold tiers and actor views"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement hot/warm/cold tiers with indexing, LRU eviction, and promotion/demotion. - - [ ] Code [Hamza]: Add tier storage backends (in-memory hot, sqlite warm, file-backed cold). - - [ ] Code [Hamza]: Add per-actor context views (strategist/executor/reviewer) and filtered presentation. - - [ ] Code [Hamza]: Add summarization hook when demoting to cold tier. - - [ ] Docs [Hamza]: Add `docs/reference/context_tiers.md`. - - [ ] Tests (Behave) [Hamza]: Add context tier scenarios. - - [ ] Tests (Robot) [Hamza]: Add context tier integration tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/context_tiers_bench.py` for tier lookup performance. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(context): add hot/warm/cold tiers and actor views"`. +#### Stage F0: Server Client Interface Stubs [Day 28-29 - REQUIRED DURING MVP] -**Parallel Group G5: Cost & Risk Estimation [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: G5.estimate) - Commit message: "feat(estimation): add cost and risk estimation actor"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Add optional `estimation_actor` role and cost/risk estimation outputs. - - [ ] Code [Hamza]: Persist estimation output to plan metadata (cost_estimate, risk_score, duration_estimate). - - [ ] Docs [Hamza]: Add `docs/reference/estimation.md` with output format. - - [ ] Tests (Behave) [Hamza]: Add estimation scenarios. - - [ ] Tests (Robot) [Hamza]: Add estimation integration smoke tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/estimation_actor_bench.py` for estimation runtime. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(estimation): add cost and risk estimation actor"`. +These stubs ensure the client architecture supports future server connectivity without implementing it: -**Parallel Group G6: CLI Polish [Jeff]** -- [ ] **COMMIT (Owner: Jeff | Group: G6.cli) - Commit message: "chore(cli): polish help and output"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Standardize help text, progress indicators, and error messages with recovery hints. - - [ ] Code [Jeff]: Ensure `--format` outputs are consistent (rich/color/table/plain/json/yaml) across core commands. - - [ ] Docs [Jeff]: Update CLI output examples where needed. - - [ ] Tests (Robot) [Jeff]: Add CLI UX smoke tests for critical commands. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/cli_render_bench.py` for output rendering overhead. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "chore(cli): polish help and output"`. +- [ ] **Stage F0: Server Client Interface Stubs** (Day 28-29) **[Luis - Required]** + - [ ] **F0.1** [Luis] Create `src/cleveragents/interfaces/server_client.py` with protocol stubs: + - [ ] `class ServerClient(Protocol):` with all method signatures for client-to-server communication + - [ ] `async def connect(server_url: str) -> None: raise NotImplementedError("Server connectivity not yet implemented")` + - [ ] `async def disconnect() -> None: raise NotImplementedError(...)` + - [ ] `async def sync_action(action: Action) -> Action: raise NotImplementedError(...)` + - [ ] `async def request_remote_execution(plan_id: str) -> str: raise NotImplementedError(...)` + - [ ] Commit: "feat(interfaces): add ServerClient protocol stub" + - [ ] **F0.2** [Luis] Create `src/cleveragents/interfaces/remote_execution_client.py`: + - [ ] `class RemoteExecutionClient(Protocol):` - protocol for requesting remote plan execution from server + - [ ] `async def submit_plan(plan_id: str, server_url: str) -> str: raise NotImplementedError(...)` + - [ ] `async def poll_status(execution_id: str) -> ExecutionStatus: raise NotImplementedError(...)` + - [ ] `async def fetch_results(execution_id: str) -> ExecutionResult: raise NotImplementedError(...)` + - [ ] Commit: "feat(interfaces): add RemoteExecutionClient protocol stub" + - [ ] **F0.3** [Luis] Create `src/cleveragents/interfaces/auth_client.py`: + - [ ] `class AuthClient(Protocol):` - protocol for client authentication with server + - [ ] `async def authenticate(credentials: Credentials) -> AuthToken: raise NotImplementedError(...)` + - [ ] `async def validate_token(token: str) -> TokenValidation: raise NotImplementedError(...)` + - [ ] `async def refresh_token(token: str) -> AuthToken: raise NotImplementedError(...)` + - [ ] Commit: "feat(interfaces): add AuthClient protocol stub" + - [ ] **F0.4** [Luis] Add `agents [--data-dir PATH] [--config-path PATH] connect` CLI command as stub: + - [ ] Add to `src/cleveragents/cli/commands/server_client.py` + - [ ] Command signature: `@click.command("connect") @click.argument("server_url")` + - [ ] Implementation: `click.echo("Server connectivity not yet implemented. Coming soon!")`; `raise SystemExit(1)` + - [ ] Commit: "feat(cli): add connect command stub" + - [ ] **F0.5** [Rui] Write minimal tests verifying stubs raise NotImplementedError: + - [ ] Test: Calling ServerClient methods raises NotImplementedError + - [ ] Test: `agents [--data-dir PATH] [--config-path PATH] connect` displays "not implemented" message and exits + - [ ] Commit: "test(behave): add server client stub tests" + +--- + +#### Stages F1-F4: Server Client Implementation [DEFERRED - Beyond Day 30] + +> **These stages are OUT OF SCOPE for the 30-day timeline.** Do not begin work on them until after Day 30 milestone is achieved. Note: These stages implement **client-side** connectivity; the server is a separate project. + +- [ ] **Stage F1: Server Client Infrastructure** (Post-Day 30) **[Luis]** **[DEFERRED]** + - [ ] **F1.1** [Luis] Create HTTP client in `src/cleveragents/infrastructure/server_client.py` + - [ ] **F1.2** [Luis] Connection health check and version negotiation + - [ ] **F1.3** [Luis] API client code generation from server OpenAPI spec + - [ ] **F1.4** [Rui] Client connection tests (with mock server) + +- [ ] **Stage F2: Plan Sync Client** (Post-Day 30) **[Luis]** **[DEFERRED]** + - [ ] **F2.1** [Luis] Sync local actions to server + - [ ] **F2.2** [Luis] Request plan creation on server + - [ ] **F2.3** [Luis] Request plan execution on server + - [ ] **F2.4** [Luis] Request `agents [--data-dir PATH] [--config-path PATH] plan apply` on server + - [ ] **F2.5** [Luis] Fetch plan status from server + - [ ] **F2.6** [Rui] Client-side API integration tests (with mock server) + +- [ ] **Stage F3: WebSocket Client** (Post-Day 30) **[Luis]** **[DEFERRED]** + - [ ] **F3.1** [Luis] WebSocket client for receiving plan updates from server + - [ ] **F3.2** [Luis] Handle phase transitions, node completions from server + - [ ] **F3.3** [Rui] WebSocket client tests (with mock server) + +- [ ] **Stage F4: Remote Project Support** (Post-Day 30) **[Hamza]** **[DEFERRED]** + - [ ] **F4.1** [Hamza] Client can specify remote resources for server execution + - [ ] **F4.2** [Hamza] Client sends execution requests to server for remote resources + - [ ] **F4.3** [Rui] End-to-end tests for remote execution (with mock server) + +**M7 SUCCESS CRITERIA** (Post-Day 30): +- [ ] `agents [--data-dir PATH] [--config-path PATH] connect ` establishes connection to an external server +- [ ] Plans can be synced and executed on a remote server +- [ ] Real-time updates received via WebSocket from server +- [ ] Remote projects can be specified and executed on server **--- MERGE POINT 2: Day 30 - Large Project Autonomy Target (LOCAL MODE ONLY) ---** By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is deferred): -- Handle projects with 10,000+ files using hierarchical decomposition. -- Port source code from one language to another autonomously. -- Use hierarchical subplans (5+ levels deep) for large tasks. -- Correct decisions at any point without full re-execution. -- Operate entirely in local mode (server client stubs in place but not implemented). +- [ ] Handle projects with 10,000+ files using hierarchical decomposition +- [ ] Port source code from one language to another autonomously +- [ ] Use hierarchical subplans (5+ levels deep) for large tasks +- [ ] Correct decisions at any point without full re-execution +- [ ] Operate entirely in local mode (server client stubs in place but not implemented) +**Note**: Server connectivity (M7 as redefined) is deferred beyond Day 30. The server is a separate project. The Day 30 goal focuses on autonomous large project handling in local mode. -### Section 9: Server Connectivity (Stubs Only) [Beyond Day 30] +--- + +### Section 9: Full Feature Set [Days 31-35] **Target: Milestone M7 (+35 days)** -**Parallel Group F0: Server Client Stubs [Luis + Rui]** (required for M6; no server implementation) -- [ ] **COMMIT (Owner: Luis | Group: F0.stubs) - Commit message: "feat(interfaces): add server client stubs"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Add protocol stubs for `ServerClient`, `RemoteExecutionClient`, and `AuthClient` with NotImplementedError. - - [ ] Code [Luis]: Add `agents connect ` CLI stub in `cli/commands/server_client.py`. - - [ ] Docs [Luis]: Add `docs/reference/server_client_stubs.md` noting client-only behavior. - - [ ] Tests (Behave) [Luis]: Add stub behavior scenarios. - - [ ] Tests (Robot) [Luis]: Add CLI stub smoke test. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/server_stub_bench.py` (baseline no-op). - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(interfaces): add server client stubs"`. +- [ ] **Stage G1: Automation Levels Enhancement** (Day 31-32) **[Luis]** + - [ ] **G1.1** [Luis] Full manual mode with decision prompts: + - [ ] Every decision point pauses for human input + - [ ] Display context, alternatives considered, recommendation + - [ ] Accept explicit choice or custom guidance + - [ ] Record user decisions in decision tree + - [ ] **G1.2** [Luis] Review-before-apply with diff display: + - [ ] AI makes all decisions autonomously during Strategize + - [ ] Execution completes in sandbox + - [ ] Human reviews complete diff before apply: + - [ ] Show changed files summary + - [ ] Show full unified diff with syntax highlighting + - [ ] Show risk warnings (auth code, migrations, etc.) + - [ ] User can approve, reject, or correct specific decisions + - [ ] **G1.3** [Luis] Full automation with confidence escalation: + - [ ] AI makes all decisions autonomously + - [ ] Execution proceeds through apply without pause + - [ ] Human notified of completion + - [ ] Rollback available if issues detected post-apply + - [ ] EVEN in full automation, critical decisions escalate: + - [ ] If confidence below threshold, request human guidance + - [ ] If touching critical files (defined by project), escalate + - [ ] If cost exceeds budget, escalate + - [ ] **G1.4** [Luis] Progressive trust building (track success rates): + - [ ] Track decision success rates per decision type + - [ ] Track codebase familiarity scores per project + - [ ] Confidence increases with successful history + - [ ] Allow automatic upgrade: manual -> review -> full + - [ ] After N successful plans in manual, suggest review mode + - [ ] After N successful plans in review, suggest full mode + - [ ] **G1.5** [Luis] Implement `AutonomyController` class: + - [ ] Method `assess_decision_confidence(decision, context) -> float` + - [ ] Method `should_escalate(decision, confidence, automation_level) -> bool` + - [ ] Method `get_historical_success(decision_type) -> float` + - [ ] Method `get_familiarity_score(project) -> float` + - [ ] **G1.6** [Rui] Tests for each automation level: + - [ ] Scenario: Manual mode pauses at each decision + - [ ] Scenario: Review-before-apply shows diff before apply + - [ ] Scenario: Full automation completes without pause + - [ ] Scenario: Low confidence in full automation escalates + - [ ] Scenario: Progressive trust upgrade suggestion shown -**M7 SUCCESS CRITERIA** (Post-Day 30): -- `agents [--data-dir PATH] [--config-path PATH] connect ` establishes connection to an external server. -- Plans can be synced and executed on a remote server. -- Real-time updates received via WebSocket from server. -- Remote projects can be specified and executed on server. +- [ ] **Stage G2: Checkpointing & Rollback** (Day 32-33) **[Luis]** + - [ ] **G2.1** [Luis] Skill-level checkpoint declarations + - [ ] **G2.2** [Luis] Plan-level rollback policy + - [ ] **G2.3** [Luis] Rollback to checkpoint command + - [ ] **G2.4** [Rui] Checkpoint/rollback tests +- [ ] **Stage G3: Semantic Validation Framework** (Day 33-34) **[Luis]** + - [ ] **G3.1** [Luis] Decision-time validation in Strategize: + - [ ] Every decision includes semantic validation + - [ ] Record `validation_performed` list on each decision + - [ ] Validate chosen option against alternatives + - [ ] Check for breaking changes, compatibility issues + - [ ] **G3.2** [Luis] Execution-time semantic guards: + - [ ] Actor configs can include validation nodes + - [ ] `validate_api_compatibility` - check for breaking API changes + - [ ] `validate_type_safety` - check types are preserved + - [ ] `auto_migrate` - generate migration plan for breaking changes + - [ ] Guards can auto-fix simple issues or escalate complex ones + - [ ] **G3.3** [Luis] Invariant enforcement system: + - [ ] User-defined invariants per project (see Section 15) + - [ ] Check invariants at each major step + - [ ] Fail execution on violation (if severity=error) + - [ ] Record invariant check results in plan metadata + - [ ] **G3.4** [Luis] Error pattern database: + - [ ] Store historical failures with context + - [ ] Identify patterns: "Async conversion in X module often causes Y" + - [ ] Before execution, check for known patterns + - [ ] Add preventive checks based on patterns + - [ ] Learn from successful corrections + - [ ] **G3.5** [Luis] Implement `SemanticValidationService`: + - [ ] Method `validate_decision(decision) -> ValidationResult` + - [ ] Method `check_invariants(project, changes) -> list[InvariantResult]` + - [ ] Method `check_error_patterns(context) -> list[PatternMatch]` + - [ ] Method `suggest_preventive_checks(pattern) -> list[Check]` + - [ ] **G3.6** [Rui] Semantic validation tests: + - [ ] Scenario: Decision with breaking API change is flagged + - [ ] Scenario: Invariant violation detected during execution + - [ ] Scenario: Known error pattern triggers preventive check + +- [ ] **Stage G4: Context Tiers** (Day 34-35) **[Hamza]** + - [ ] **G4.1** [Hamza] Hot context management (10-20 files): + - [ ] Track files currently in LLM context window + - [ ] Implement LRU eviction when context limit reached + - [ ] Prioritize files based on current task relevance + - [ ] Support explicit pinning of critical files + - [ ] **G4.2** [Hamza] Warm context with vector search: + - [ ] Recent decisions and their contexts from current plan tree + - [ ] Indexed embeddings from project files + - [ ] Vector search results for quick retrieval + - [ ] Decision chain that led to current work + - [ ] **G4.3** [Hamza] Cold context for historical decisions: + - [ ] Historical decisions from past plans on this codebase + - [ ] Past refactoring patterns ("last time we did X...") + - [ ] Cross-project learnings (if enabled) + - [ ] Queryable but not in active memory + - [ ] **G4.4** [Hamza] Per-actor context views: + - [ ] Implement `ActorContextView` service + - [ ] Strategist view: architecture docs, READMEs, module boundaries, dependency graphs + - [ ] Executor view: precise code sections for edits, test files + - [ ] Reviewer view: diffs, tests, risk zones, style guides + - [ ] Each actor gets filtered view based on role + - [ ] **G4.5** [Hamza] Implement promotion/demotion algorithms: + - [ ] Analyze current query/task + - [ ] Promote relevant data upward (cold -> warm -> hot) + - [ ] Demote stale data out of hot to keep prompts tight + - [ ] Preserve complete context snapshots for every decision + - [ ] **G4.6** [Rui] Context tier tests: + - [ ] Scenario: Hot context respects file limit + - [ ] Scenario: Warm context includes recent decisions + - [ ] Scenario: Actor receives role-appropriate context view + - [ ] Scenario: Promotion moves relevant cold data to warm + +- [ ] **Stage G5: Cost & Risk Estimation** (Day 35) **[Hamza]** + - [ ] **G5.1** [Hamza] Estimation actor implementation: + - [ ] Create optional `estimation_actor` role in action model + - [ ] Estimation actor runs after Strategize completes, before Execute + - [ ] Input: strategy output, project context + - [ ] Output: cost estimate, risk assessment, time estimate + - [ ] **G5.2** [Hamza] Token/cost estimation: + - [ ] Analyze strategy to estimate number of LLM calls + - [ ] Estimate tokens per call based on context size + - [ ] Map model costs to get dollar estimate + - [ ] Estimate number of steps/subplans + - [ ] Provide confidence interval (min/expected/max) + - [ ] **G5.3** [Hamza] Risk assessment: + - [ ] Analyze strategy for risky operations: + - [ ] Touching auth/security code + - [ ] Database migrations + - [ ] Public API changes + - [ ] Infrastructure changes + - [ ] Calculate risk score (low/medium/high) + - [ ] Identify specific risk factors + - [ ] Estimate likelihood of rollback needed + - [ ] **G5.4** [Hamza] Display estimation before execute: + - [ ] Show estimated cost before user confirms execute + - [ ] Support `--yes` to bypass confirmation in automation contexts + - [ ] Show risk assessment summary + - [ ] Allow user to abort if cost too high + - [ ] **G5.5** [Rui] Estimation tests: + - [ ] Scenario: Estimation actor produces cost estimate + - [ ] Scenario: High-risk strategy flagged appropriately + - [ ] Scenario: User can abort based on estimate + +- [ ] **Stage G6: Full CLI Polish** (Day 35) **[All]** + - [ ] **G6.1** [All] Consistent help text + - [ ] **G6.2** [All] Rich terminal output + - [ ] **G6.3** [All] Progress indicators + - [ ] **G6.4** [All] Error messages with recovery suggestions + +**M7 SUCCESS CRITERIA**: +- [ ] All spec features implemented +- [ ] Full test coverage (>85%) +- [ ] Documentation complete +- [ ] Ready for release + +--- ### Section 10: Async Infrastructure & Later-Stage Quality Work [Various Leads] **Note**: Quality automation setup is in Section 0; Section 10 focuses on async infrastructure and later-stage validation support. -**Parallel Group 10A: Async Infrastructure [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: 10A.async) - Commit message: "feat(async): add async command execution and workers"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Implement async command execution per ADR-002 with cancellation and timeout handling. - - [ ] Code [Luis]: Add `AsyncJob` model and `async_jobs` table (plan_id, phase, status, payload_json, created_at, started_at, finished_at). - - [ ] Code [Luis]: Add AsyncWorker orchestrator with polling loop, max_workers config, and graceful shutdown hooks. - - [ ] Code [Luis]: Add job enqueue hooks for plan execute/apply when async is enabled via config flag (no new CLI flags). - - [ ] Code [Luis]: Add cancellation token support and ensure cancellation propagates to tool execution. - - [ ] Docs [Luis]: Update `docs/reference/async_architecture.md` with execution flow, job states, and shutdown rules. - - [ ] Tests (Behave) [Luis]: Add `features/async_execution.feature` for async command handling (enqueue, worker pick-up, cancel). - - [ ] Tests (Robot) [Luis]: Add `robot/async_execution.robot` smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/async_execution_bench.py` for worker scheduling overhead. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(async): add async command execution and workers"`. -- [ ] **COMMIT (Owner: Luis | Group: 10A.retry) - Commit message: "feat(async): wire retry policies into services"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Integrate retry/circuit breaker policies into service layer operations. - - [ ] Code [Luis]: Add retry policy configuration keys (max_attempts, base_delay, max_delay, jitter) to settings. - - [ ] Code [Luis]: Ensure retries are only applied to idempotent operations (repository reads, validation calls) and never to applies. - - [ ] Docs [Luis]: Document retry policy defaults and override points. - - [ ] Tests (Behave) [Luis]: Add retry/circuit breaker behavior scenarios. - - [ ] Tests (Robot) [Luis]: Add resilience smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/retry_policy_bench.py` for retry overhead. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(async): wire retry policies into services"`. +- [ ] **Stage 10A: Async Infrastructure** (Days 10-12) **[Luis]** + - [ ] Code: Implement async patterns per ADR-002 + - [ ] **10A.1** [Luis] Implement async command execution + - [X] **10A.2** Implement the 33 retry patterns with tenacity (COMPLETED 2025-11-17) + - [X] **10A.3** Add circuit breaker for failures (COMPLETED 2025-11-17) + - [ ] **10A.4** [Luis] Add background workers (convert 7 concurrency patterns to asyncio tasks) + - [ ] **10A.5** [Luis] Integrate retry patterns into new services -**Parallel Group 10B: Selective Quality Review [Brent]** -- [ ] **COMMIT (Owner: Brent | Group: 10B.review) - Commit message: "docs(qa): add review playbook and priority matrix"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Docs [Brent]: Create `docs/development/review_playbook.md` with focus areas and skip rules. - - [ ] Docs [Brent]: Add priority matrix and review SLA guidance. - - [ ] Docs [Brent]: Add checklist templates for architecture review, CLI review, and DB migration review. - - [ ] Tests (Behave) [Brent]: Add scenarios validating review playbook references exist. - - [ ] Tests (Robot) [Brent]: Add docs build smoke test covering the new guide. - - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/docs_build_bench.py` for docs build baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Brent]: `git commit -m "docs(qa): add review playbook and priority matrix"`. +- [ ] **Stage 10B: Selective Quality Review** (Days 4-8) **[Brent]** + - [ ] Focus Areas: Only review critical items + - [ ] **10B.1** [Brent] Review architectural decisions in PRs: + - [ ] Service layer design choices + - [ ] Database schema decisions + - [ ] API contract definitions + - [ ] Skip: formatting, simple CRUD, test files + - [ ] **10B.2** [Brent] Review complex algorithms: + - [ ] Decision tree traversal logic + - [ ] Merge conflict resolution + - [ ] Dependency closure computation + - [ ] Skip: straightforward implementations + - [ ] **10B.3** [Brent] Review security-sensitive code: + - [ ] Authentication/authorization + - [ ] Input validation + - [ ] Sandbox boundaries + - [ ] Skip: code already scanned by bandit + - [ ] **10B.4** [Brent] Monitor automated quality metrics: + - [ ] Daily check of CI/CD dashboard + - [ ] Weekly quality report generation + - [ ] Escalate only if metrics drop -**Parallel Group 10C: Validation Testing Support [Brent + Luis]** -- [ ] **COMMIT (Owner: Brent | Group: 10C.edge) - Commit message: "test(validation): add edge case suites"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Brent]: Add shared edge-case fixtures under `features/fixtures/validation/`. - - [ ] Code [Brent]: Add fixtures for malformed tool outputs, missing resources, and validation timeouts. - - [ ] Docs [Brent]: Update `docs/development/testing.md` with validation test catalog. - - [ ] Tests (Behave) [Brent]: Add edge-case scenarios for concurrency, conflicts, rollbacks, and timeouts. - - [ ] Tests (Robot) [Brent]: Add integration coverage for edge-case suites. - - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/validation_edge_bench.py` for edge-case runtime. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Brent]: `git commit -m "test(validation): add edge case suites"`. -- [ ] **COMMIT (Owner: Luis | Group: 10C.semantic) - Commit message: "test(validation): add semantic validation suites"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Add semantic validation fixtures and error-pattern samples. - - [ ] Code [Luis]: Add fixtures for language-porting mismatches and dependency graph violations. - - [ ] Docs [Luis]: Document semantic validation coverage expectations. - - [ ] Tests (Behave) [Luis]: Add semantic validation scenarios. - - [ ] Tests (Robot) [Luis]: Add semantic validation integration tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/semantic_validation_suite_bench.py` for suite runtime. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "test(validation): add semantic validation suites"`. -- [ ] **COMMIT (Owner: Brent | Group: 10C.performance) - Commit message: "test(perf): add scale test fixtures"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Brent]: Add scale fixtures for 1K/5K/10K file repos in `features/fixtures/scale/`. - - [ ] Code [Brent]: Add scriptless fixture generator instructions (documented, no helper scripts). - - [ ] Docs [Brent]: Add scale test runbook and environment notes. - - [ ] Tests (Behave) [Brent]: Add scale test scenarios validating thresholds. - - [ ] Tests (Robot) [Brent]: Add large-project Robot tests for performance runs. - - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/scale_fixture_bench.py` for baseline performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Brent]: `git commit -m "test(perf): add scale test fixtures"`. +- [ ] **Stage 10C: Validation Testing Support** (Days 9-30) **[Brent + Luis]** + - [ ] High-impact testing work + - [ ] **10C.1** [Brent] Create edge case test scenarios: + - [ ] Concurrent plan execution edge cases + - [ ] Resource conflict scenarios + - [ ] Validation failure chains + - [ ] Rollback edge cases + - [ ] **10C.2** [Brent + Luis] Implement semantic validation tests: + - [ ] API compatibility validation + - [ ] Business invariant preservation + - [ ] Cross-resource consistency + - [ ] **10C.3** [Brent] Performance testing for scale: + - [ ] 10K+ file repository handling + - [ ] Memory usage profiling + - [ ] Context tier performance + - [ ] **10C.4** [Brent] Create validation test fixtures: + - [ ] Invalid code samples + - [ ] Edge case project structures + - [ ] Malformed input data --- @@ -2703,407 +8318,1057 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is **Target: Throughout project, critical items by Day 14** -**Note**: Security tasks focus on runtime protections; quality gates are handled in Section 0. +**Note**: With automated quality gates in place (Section 0), Brent only reviews security changes after automated scanning. -**Parallel Group SEC1: Remove eval() usage [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: SEC1.eval) - Commit message: "fix(security): remove eval-based config parsing"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Audit and remove all `eval`/`exec`/`compile` usage from production config paths. - - [ ] Docs [Luis]: Add `docs/reference/security_eval.md` with replacement patterns. - - [ ] Tests (Behave) [Luis]: Add `features/security_eval.feature` scenarios. - - [ ] Tests (Robot) [Luis]: Add `robot/security_eval.robot` smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/security_eval_bench.py` for config parsing baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "fix(security): remove eval-based config parsing"`. +- [ ] **Stage SEC1: Remove eval() Vulnerability** (Day 1-2) **[Luis - CRITICAL]** + - [ ] Test with code containing unused imports + - [ ] Commit: "feat(qa): add ruff linting to pre-commit" + - [ ] **0.1e** [Brent] Add pyright type checking hook: + ```yaml + - repo: local + hooks: + - id: pyright + name: Type check with pyright + entry: pyright + language: system + types: [python] + require_serial: true + ``` + - [ ] Ensure pyright is installed via dev dependencies + - [ ] Test with code containing type errors + - [ ] Commit: "feat(qa): add pyright to pre-commit" + - [ ] **0.1f** [Brent] Add security scanning with bandit: + - [ ] Add `bandit[toml]>=1.7.5` to dev dependencies + - [ ] Create `pyproject.toml` section for bandit config: + ```toml + [tool.bandit] + exclude_dirs = ["tests", "features", "benchmarks"] + skips = ["B101"] # Skip assert_used test in test files + ``` + - [ ] Add bandit hook: + ```yaml + - repo: https://github.com/PyCQA/bandit + rev: '1.7.5' + hooks: + - id: bandit + args: ['-c', 'pyproject.toml'] + additional_dependencies: ["bandit[toml]"] + ``` + - [ ] Commit: "feat(qa): add bandit security scanning" + - [ ] **0.1g** [Brent] Add vulture for dead code detection: + - [ ] Add `vulture>=2.10` to dev dependencies + - [ ] Create `vulture_whitelist.py` for false positives + - [ ] Add vulture hook: + ```yaml + - repo: local + hooks: + - id: vulture + name: Find dead code with vulture + entry: vulture + language: system + types: [python] + args: [--min-confidence, "80", "--exclude", "*/tests/*,*/features/*"] + ``` + - [ ] Commit: "feat(qa): add vulture dead code detection" + - [ ] **0.1h** [Brent] Add test runner hook for changed files: + ```yaml + - repo: local + hooks: + - id: pytest-changed + name: Run tests for changed files + entry: bash -c 'git diff --cached --name-only | grep -E "\.py$" | xargs -I {} pytest tests/{} 2>/dev/null || true' + language: system + pass_filenames: false + always_run: true + ``` + - [ ] Commit: "feat(qa): add test runner for changed files" + - [ ] **0.1i** [Brent] Add semgrep for pattern-based checks: + - [ ] Install semgrep: Add `semgrep>=1.45.0` to dev dependencies + - [ ] Create `.semgrep.yml` with initial rules: + ```yaml + rules: + - id: no-eval + pattern: eval(...) + message: "eval() is dangerous and banned" + languages: [python] + severity: ERROR + - id: no-exec + pattern: exec(...) + message: "exec() is dangerous and banned" + languages: [python] + severity: ERROR + - id: no-bare-except + pattern: | + try: + ... + except: + ... + message: "Use specific exception types" + languages: [python] + severity: WARNING + ``` + - [ ] Add semgrep hook: + ```yaml + - repo: local + hooks: + - id: semgrep + name: Scan with semgrep + entry: semgrep --config=.semgrep.yml + language: system + types: [python] + ``` + - [ ] Commit: "feat(qa): add semgrep pattern scanning" + - [ ] **0.1j** [Brent] Add commit message linting: + ```yaml + - repo: https://github.com/commitizen-tools/commitizen + rev: v3.13.0 + hooks: + - id: commitizen + stages: [commit-msg] + ``` + - [ ] Configure conventional commits format + - [ ] Commit: "feat(qa): add commit message linting" + - [ ] **0.1k** [Brent] Create developer setup script `scripts/setup-dev.sh`: + ```bash + #!/bin/bash + set -euo pipefail + echo "Setting up pre-commit hooks..." + pip install pre-commit + pre-commit install + pre-commit install --hook-type commit-msg + echo "Running initial quality checks..." + pre-commit run --all-files + echo "Developer environment ready!" + ``` + - [ ] Make executable: `chmod +x scripts/setup-dev.sh` + - [ ] Update README.md developer setup instructions + - [ ] Commit: "feat(qa): add developer setup script" + + **Day 2: CI/CD Pipeline with GitHub Actions (or GitLab CI equivalent)** + + - [ ] **0.2** [Brent] Create GitHub Actions workflow for PR validation: + - [ ] **0.2a** [Brent] Create `.github/workflows/pr-validation.yml`: + ```yaml + name: PR Validation + on: + pull_request: + types: [opened, synchronize, reopened] + jobs: + quality-gates: + name: Quality Gates + runs-on: ubuntu-latest + ``` + - [ ] Commit: "feat(ci): add PR validation workflow scaffold" + - [ ] **0.2b** [Brent] Add Python setup and dependency caching: + ```yaml + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # For proper git history + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Cache pip packages + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-pip- + ``` + - [ ] Commit: "feat(ci): add Python setup with caching" + - [ ] **0.2c** [Brent] Install dependencies and run formattin check: + ```yaml + - name: Install dependencies + run: | + pip install -e .[dev,tests] + pip install pre-commit + + - name: Check code formatting + run: | + pre-commit run ruff-format --all-files --show-diff-on-failure + ``` + - [ ] Commit: "feat(ci): add formatting check" + - [ ] **0.2d** [Brent] Add linting step: + ```yaml + - name: Lint with Ruff + run: | + ruff check . --output-format=github + ``` + - [ ] Use GitHub annotations format for inline PR comments + - [ ] Commit: "feat(ci): add linting with GitHub annotations" + - [ ] **0.2e** [Brent] Add type checking step: + ```yaml + - name: Type check with pyright + run: | + pyright --outputjson > pyright-results.json || true + python scripts/parse-pyright-results.py pyright-results.json + ``` + - [ ] Create `scripts/parse-pyright-results.py` to format errors as GitHub annotations + - [ ] Commit: "feat(ci): add type checking with annotations" + - [ ] **0.2f** [Brent] Add security scanning: + ```yaml + - name: Security scan with bandit + run: | + bandit -r src/ -f json -o bandit-results.json || true + python scripts/parse-bandit-results.py bandit-results.json + + - name: Check for vulnerabilities + run: | + pip install safety + safety check --json > safety-results.json || true + python scripts/parse-safety-results.py safety-results.json + ``` + - [ ] Create parsing scripts for annotations + - [ ] Commit: "feat(ci): add security scanning" + - [ ] **0.2g** [Brent] Add test execution with coverage: + ```yaml + - name: Run tests with coverage + run: | + nox -s unit_tests -- --junit-xml=test-results.xml + nox -s coverage_report + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results + path: test-results.xml + + - name: Comment coverage on PR + uses: py-cov-action/python-coverage-comment-action@v3 + with: + GITHUB_TOKEN: ${{ github.token }} + MINIMUM_GREEN: 85 + MINIMUM_ORANGE: 70 + ``` + - [ ] Commit: "feat(ci): add test execution with coverage reporting" + - [ ] **0.2h** [Brent] Add semgrep scanning: + ```yaml + - name: Semgrep scan + uses: returntocorp/semgrep-action@v1 + with: + config: .semgrep.yml + generateSarif: true + + - name: Upload SARIF + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: semgrep.sarif + ``` + - [ ] Commit: "feat(ci): add semgrep scanning with SARIF" + - [ ] **0.2i** [Brent] Add PR comment summary: + ```yaml + - name: Generate quality report + if: always() + run: | + python scripts/generate-quality-report.py \ + --coverage coverage.xml \ + --pyright pyright-results.json \ + --bandit bandit-results.json \ + --output pr-comment.md + + - name: Comment on PR + if: always() + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const comment = fs.readFileSync('pr-comment.md', 'utf8'); + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment + }); + ``` + - [ ] Create `scripts/generate-quality-report.py` to aggregate results + - [ ] Commit: "feat(ci): add PR quality report comment" + - [ ] **0.2j** [Brent] Add job failure conditions: + ```yaml + - name: Check quality gates + run: | + python scripts/check-quality-gates.py \ + --coverage-min 85 \ + --type-errors-max 0 \ + --security-issues-max 0 + ``` + - [ ] Script exits with code 1 if any gate fails + - [ ] Commit: "feat(ci): add quality gate enforcement" + - [ ] **0.2k** [Brent] Create branch protection rules documentation: + - [ ] Document in `docs/development/branch-protection.md`: + - [ ] Require PR validation workflow to pass + - [ ] Require at least 1 review (Brent reviews all) + - [ ] Dismiss stale reviews on new commits + - [ ] Require branches to be up to date before merging + - [ ] Commit: "docs(qa): add branch protection documentation" + + **Day 3: Advanced Automation and Monitoring** + + - [ ] **0.3** [Brent] Set up advanced quality automation: + - [ ] **0.3a** [Brent] Create nightly quality check workflow `.github/workflows/nightly-quality.yml`: + ```yaml + name: Nightly Quality Check + on: + schedule: + - cron: '0 0 * * *' # Run at midnight UTC + workflow_dispatch: # Allow manual trigger + ``` + - [ ] Run full test suite including slow tests + - [ ] Run mutation testing with mutmut + - [ ] Generate comprehensive reports + - [ ] Commit: "feat(ci): add nightly quality checks" + - [ ] **0.3b** [Brent] Add dependency update automation: + ```yaml + name: Dependency Updates + on: + schedule: + - cron: '0 9 * * MON' # Weekly on Mondays + jobs: + update-deps: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Update dependencies + run: | + pip install pip-tools + pip-compile --upgrade + pip install .[dev,tests] + pre-commit autoupdate + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + with: + title: "chore: update dependencies" + body: "Automated dependency updates" + branch: deps/automated-update + ``` + - [ ] Commit: "feat(ci): add dependency update automation" + - [ ] **0.3c** [Brent] Create complexity monitoring: + - [ ] Install `radon>=6.0.1` for complexity analysis + - [ ] Add to pre-commit: + ```yaml + - repo: local + hooks: + - id: complexity-check + name: Check code complexity + entry: radon cc src/ -nb -s + language: system + pass_filenames: false + ``` + - [ ] Add to CI pipeline with threshold enforcement + - [ ] Commit: "feat(qa): add complexity monitoring" + - [ ] **0.3d** [Brent] Set up performance regression detection: + - [ ] Create `benchmarks/` directory with ASV benchmarks + - [ ] Add benchmark job to CI: + ```yaml + - name: Run benchmarks + run: | + asv machine --yes + asv run HEAD^..HEAD + asv compare HEAD^ HEAD + ``` + - [ ] Fail if performance regresses >10% + - [ ] Commit: "feat(ci): add performance regression detection" + - [ ] **0.3e** [Brent] Create quality metrics dashboard script: + - [ ] Script `scripts/generate-metrics-dashboard.py`: + - [ ] Aggregate coverage trends + - [ ] Track type checking progress + - [ ] Monitor code complexity trends + - [ ] Count TODO/FIXME/HACK comments + - [ ] Generate markdown report + - [ ] Run weekly and post to team + - [ ] Commit: "feat(qa): add quality metrics dashboard" + - [ ] **0.3f** [Brent] Set up documentation quality checks: + - [ ] Add doc linting: + ```yaml + - repo: local + hooks: + - id: doc-quality + name: Check documentation quality + entry: python scripts/check-docstrings.py + language: system + types: [python] + ``` + - [ ] Verify all public functions have docstrings + - [ ] Check docstring format (Google style) + - [ ] Commit: "feat(qa): add documentation quality checks" + - [ ] **0.3g** [Brent] Create ADR compliance checker: + - [ ] Script `scripts/check-adr-compliance.py`: + - [ ] Parse ADRs from `docs/architecture/decisions/` + - [ ] Check code against ADR requirements + - [ ] Flag violations (e.g., sync code in async modules) + - [ ] Add to pre-commit and CI + - [ ] Commit: "feat(qa): add ADR compliance checking" + - [ ] **0.3h** [Brent] Set up coverage delta checking: + - [ ] Modify CI to track coverage changes: + ```yaml + - name: Check coverage delta + run: | + git fetch origin main + nox -s coverage_report -- --compare-branch=origin/main + python scripts/check-coverage-delta.py --min-delta=-0.5 + ``` + - [ ] Fail if coverage drops more than 0.5% + - [ ] Commit: "feat(ci): add coverage delta enforcement" + - [ ] **0.3i** [Brent] Create PR template with quality checklist: + - [ ] Create `.github/pull_request_template.md`: + ```markdown + ## Description + Brief description of changes + + ## Quality Checklist + - [ ] Tests added/updated for new functionality + - [ ] Type hints added for all new functions + - [ ] Docstrings added/updated + - [ ] No new linting warnings + - [ ] Coverage maintained or increased + - [ ] ADRs followed + - [ ] Security implications considered + + ## Testing + How has this been tested? + ``` + - [ ] Commit: "feat(qa): add PR template with quality checklist" + - [ ] **0.3j** [Brent] Document quality automation setup: + - [ ] Create `docs/development/quality-automation.md`: + - [ ] Pre-commit hook reference + - [ ] CI/CD pipeline overview + - [ ] How to run quality checks locally + - [ ] How to handle quality gate failures + - [ ] Exemption process (when needed) + - [ ] Add to developer onboarding + - [ ] Commit: "docs(qa): document quality automation" + + **Post Day 3: Transition Plan** + + - [ ] **0.4** [Brent] Transition to selective manual review (Days 4-8): + - [ ] **0.4a** [Brent] Focus manual reviews on: + - [ ] Architectural decisions + - [ ] Complex algorithms + - [ ] API contracts + - [ ] Security-sensitive code + - [ ] Skip reviewing: formatting, basic types, simple CRUD + - [ ] **0.4b** [Brent] Create review priority matrix: + - [ ] P0: Security, API changes, architecture + - [ ] P1: Complex business logic, algorithms + - [ ] P2: Normal features + - [ ] P3: Tests, docs, refactoring (trust automation) + + - [ ] **0.5** [Brent] Transition to high-impact work (After Day 8): + - [ ] **0.5a** [Luis + Brent] Move to validation pipeline work: + - [ ] Help implement semantic validation in execution actors + - [ ] Create validation test suites + - [ ] Document validation patterns + - [ ] **0.5b** [Luis + Brent] Assist with change tracking edge cases: + - [ ] Test tool-based change tracking thoroughly + - [ ] Find and fix edge cases + - [ ] Create comprehensive test scenarios -**Parallel Group SEC2: Template Injection Prevention [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: SEC2.template) - Commit message: "fix(security): harden template rendering"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Replace unsafe template usage with a sandboxed renderer and strict token set. - - [ ] Docs [Luis]: Add `docs/reference/template_security.md` with safe patterns. - - [ ] Tests (Behave) [Luis]: Add `features/security_templates.feature` scenarios. - - [ ] Tests (Robot) [Luis]: Add `robot/security_templates.robot` smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/security_template_bench.py` for render baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "fix(security): harden template rendering"`. -**Parallel Group SEC3: Exception Handling Audit [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: SEC3.exceptions) - Commit message: "fix(security): enforce explicit exception handling"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Replace silent exception handling with explicit errors and context propagation. - - [ ] Docs [Luis]: Document error propagation standards and logging rules. - - [ ] Tests (Behave) [Luis]: Add `features/security_exceptions.feature` scenarios. - - [ ] Tests (Robot) [Luis]: Add exception handling integration smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/security_exception_bench.py` for error path overhead baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "fix(security): enforce explicit exception handling"`. -**Parallel Group SEC4: Async Lifecycle Correctness [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: SEC4.async) - Commit message: "fix(security): close async resources and leaks"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Close async resources, checkpoint files, and subscription leaks with retention policies. - - [ ] Docs [Luis]: Add `docs/reference/async_safety.md` on cleanup rules. - - [ ] Tests (Behave) [Luis]: Add `features/security_async.feature` scenarios. - - [ ] Tests (Robot) [Luis]: Add async cleanup integration tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/security_async_cleanup_bench.py` for cleanup overhead baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "fix(security): close async resources and leaks"`. -**Parallel Group SEC5: Secrets Management [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: SEC5.secrets) - Commit message: "feat(security): add secrets masking and validation"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Mask credentials in logs, validate required keys, and block secret leakage in outputs. - - [ ] Docs [Hamza]: Add `docs/reference/secrets_handling.md`. - - [ ] Tests (Behave) [Hamza]: Add `features/security_secrets.feature` scenarios. - - [ ] Tests (Robot) [Hamza]: Add secrets handling integration smoke tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/security_secrets_bench.py` for masking overhead baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(security): add secrets masking and validation"`. -**Parallel Group SEC6: Read-Only Enforcement [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: SEC6.readonly) - Commit message: "feat(security): enforce read-only actions"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Validate read-only actions only use read-only skills at execution time. - - [ ] Docs [Luis]: Add `docs/reference/read_only_actions.md`. - - [ ] Tests (Behave) [Luis]: Add `features/security_readonly.feature` scenarios. - - [ ] Tests (Robot) [Luis]: Add read-only enforcement integration tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/security_readonly_bench.py` for enforcement overhead baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(security): enforce read-only actions"`. - - [ ] Note: Safety profile enforcement is deferred; see Section 18 POST.safety. +--- -**Parallel Group SEC7: Audit Logging [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: SEC7.audit) - Commit message: "feat(security): add audit logging for apply"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Add audit log model, migration, and `agents audit list` CLI command. - - [ ] Docs [Hamza]: Add `docs/reference/audit_logging.md`. - - [ ] Tests (Behave) [Hamza]: Add `features/security_audit.feature` scenarios. - - [ ] Tests (Robot) [Hamza]: Add audit logging integration tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/security_audit_bench.py` for log write overhead baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(security): add audit logging for apply"`. +### Section 11: Security & Safety [WORKSTREAM F - Luis + Brent] + +**Target: Throughout project, critical items by Day 14** + +**CRITICAL SECURITY BLOCKERS** (Must be addressed before any production use) + +- [ ] **Stage SEC1: Remove eval() Vulnerability** (Day 1-2) **[Luis - CRITICAL]** + - [ ] Code: Remove all eval() from config parsing + - [ ] **SEC1.1** [Luis] Audit all files for `eval()` usage in `src/cleveragents/`: + - [ ] Search for `eval(`, `exec(`, `compile(` calls + - [ ] Document each occurrence with file path and line number + - [ ] Classify as: (a) test-only, (b) removable, (c) requires redesign + - [ ] **SEC1.2** [Luis] Replace eval-based config transforms: + - [ ] Create whitelist of allowed transform operators + - [ ] Implement safe expression parser (no arbitrary code execution) + - [ ] Or: transforms must reference named functions from a registry + - [ ] **SEC1.3** [Luis] Remove eval from reactive routing config: + - [ ] Audit `src/cleveragents/reactive/config_parser.py` + - [ ] Replace dynamic code execution with safe alternatives + - [ ] **SEC1.4** [Brent] Code review all eval removal changes + - [ ] Tests: Security tests + - [ ] **SEC1.5** [Rui] Write Behave scenarios in `features/security_eval.feature`: + - [ ] Scenario: Config with code injection attempt is rejected + - [ ] Scenario: Malicious transform expression does not execute + - [ ] Scenario: Valid config still works after eval removal + +- [ ] **Stage SEC2: Template Injection Prevention** (Day 3-4) **[Luis]** + - [ ] Code: Secure template rendering + - [ ] **SEC2.1** [Luis] Replace `str.format()` with safe template engine: + - [ ] Use Jinja2 with sandboxed environment + - [ ] Or: restrict token set severely (only `{variable}` substitution) + - [ ] Prevent access to `__class__`, `__globals__`, etc. + - [ ] **SEC2.2** [Luis] Implement input sanitization for prompts: + - [ ] Treat user input as data, not instruction overrides + - [ ] Escape special characters in user-provided text + - [ ] Strict role separation in prompts (system vs user) + - [ ] **SEC2.3** [Luis] Add prompt injection mitigations: + - [ ] Detect common injection patterns + - [ ] Warn or reject suspicious inputs + - [ ] Log potential injection attempts + - [ ] Tests: Template security tests + - [ ] **SEC2.4** [Rui] Write Behave scenarios in `features/security_templates.feature`: + - [ ] Scenario: Template with Jinja2 injection attempt fails safely + - [ ] Scenario: User input with special characters is escaped + - [ ] Scenario: Prompt injection attempt is detected + +- [ ] **Stage SEC3: Exception Handling Audit** (Day 4-5) **[Luis]** + - [ ] Code: Stop swallowing exceptions (automated checks + manual fixes) + - [ ] **SEC3.1** [Luis] Run automated exception handling audit: + - [ ] Use semgrep rules to find bare `except:` or `except Exception:` + - [ ] Use vulture to find unreachable exception handlers + - [ ] Document each occurrence for manual review + - [ ] **SEC3.2** [Luis] Fix silent exception handling: + - [ ] Capture exception details + - [ ] Attach to message metadata or error state + - [ ] Fail the stream/plan with clear error state + - [ ] Log at appropriate level (error, not debug) + - [ ] **SEC3.3** [Luis] Add error context propagation: + - [ ] Errors should include stack trace reference + - [ ] Errors should identify the component that failed + - [ ] Errors should suggest recovery actions where possible + - [ ] **SEC3.4** [Brent] Review complex exception handling patterns only: + - [ ] Review async exception handling edge cases + - [ ] Validate error propagation across actor boundaries + - [ ] Tests: Exception handling tests + - [ ] **SEC3.5** [Rui] Write Behave scenarios in `features/security_exceptions.feature`: + - [ ] Scenario: Component failure surfaces as clear error + - [ ] Scenario: Error includes actionable information + - [ ] Scenario: No silent failures in normal operation + +- [ ] **Stage SEC4: Async Lifecycle Correctness** (Day 5-6) **[Luis]** + - [ ] Code: Fix async resource leaks (automated detection + fixes) + - [ ] **SEC4.1** [Luis] Run automated async pattern detection: + - [ ] Use semgrep to find all `asyncio.new_event_loop()` calls + - [ ] Use custom linter to detect unclosed resources + - [ ] Generate report of potential resource leaks + - [ ] **SEC4.2** [Luis] Fix RxPy subscription leaks: + - [ ] Ensure subscriptions are disposed on shutdown + - [ ] Dispose subscriptions on stream reconfiguration + - [ ] Track active subscriptions for debugging + - [ ] **SEC4.3** [Luis] Fix LangGraph checkpoint file leaks: + - [ ] Audit checkpoint file creation + - [ ] Implement cleanup for old checkpoint files + - [ ] Add retention policy for checkpoints + - [ ] **SEC4.4** [Brent] Selective review of async patterns: + - [ ] Review only complex async state machines + - [ ] Validate concurrent access patterns + - [ ] Tests: Resource leak tests + - [ ] **SEC4.5** [Rui] Write Behave scenarios in `features/security_async.feature`: + - [ ] Scenario: Long-running process does not leak memory + - [ ] Scenario: Shutdown cleans up all subscriptions + - [ ] Scenario: Checkpoint files cleaned after retention period + +- [ ] **Stage SEC5: Secrets Management** (Day 6-7) **[Hamza]** + - [ ] Code: Secure credential handling + - [ ] **SEC5.1** [Hamza] Implement secrets masking in logs: + - [ ] Detect API keys in log output + - [ ] Mask sensitive values (show only last 4 chars) + - [ ] Never log full credentials + - [ ] **SEC5.2** [Hamza] Secure environment variable handling: + - [ ] Validate API key format before use + - [ ] Clear error if required key missing + - [ ] Support secrets from file (for containerized environments) + - [ ] **SEC5.3** [Hamza] Prevent secrets in generated code: + - [ ] Detect hardcoded API keys in LLM output + - [ ] Warn if generated code contains potential secrets + - [ ] Block apply if secrets detected without override + - [ ] Tests: Secrets management tests + - [ ] **SEC5.4** [Rui] Write Behave scenarios in `features/security_secrets.feature`: + - [ ] Scenario: API key in log output is masked + - [ ] Scenario: Generated code with hardcoded key is flagged + - [ ] Scenario: Missing API key produces clear error + +- [ ] **Stage SEC6: Read-Only Action Enforcement** (Day 7-8) **[Luis]** + - [ ] Code: Enforce read_only actions + - [ ] **SEC6.1** [Luis] Add skill metadata validation at execution time: + - [ ] Check if action has `read_only: true` + - [ ] If read_only, verify all skills have `read_only: true` metadata + - [ ] Block execution if write skill detected in read-only action + - [ ] **SEC6.2** [Luis] Implement safety profile validation (DEFERRED to post-30; see Stage POST1): + - [ ] Action can specify `safety_profile` with: + - [ ] `allowed_skill_categories: list[str] | None` + - [ ] `denied_skill_categories: list[str] | None` + - [ ] `require_checkpoints: bool` + - [ ] `require_sandbox: bool` + - [ ] `require_human_approval: bool` (apply approval gate) + - [ ] `max_cost_usd: float | None` + - [ ] `max_retries: int` + - [ ] Add action-create CLI flags to populate SafetyProfile: + - [ ] `--require-sandbox` + - [ ] `--require-checkpoints` + - [ ] `--require-apply-approval` + - [ ] `--allow-skill-category ` (repeatable) + - [ ] `--deny-skill-category ` (repeatable) + - [ ] `--max-cost-usd ` + - [ ] `--max-retries ` + - [ ] Enforce safety profile at execution time + - [ ] Tests: Safety enforcement tests + - [ ] **SEC6.3** [Rui] Write Behave scenarios in `features/security_readonly.feature`: + - [ ] Scenario: Read-only action blocked from using write skill + - [ ] Scenario: Safety profile with require_sandbox enforced + - [ ] Scenario: Safety profile with require_checkpoints enforced + +- [ ] **Stage SEC7: Audit Logging** (Day 8-9) **[Hamza]** + - [ ] Code: Comprehensive audit trail + - [ ] **SEC7.1** [Hamza] Implement apply audit logging: + - [ ] Record who applied, what changed, when, why + - [ ] Include plan ID, action ID, project ID + - [ ] Include changeset summary (files affected) + - [ ] Store in audit table with retention policy + - [ ] **SEC7.2** [Hamza] Create Alembic migration for `audit_log` table: + - [ ] Schema: `audit_id`, `event_type`, `user_id`, `plan_id`, `details`, `created_at` + - [ ] Index on `event_type`, `plan_id`, `created_at` + - [ ] **SEC7.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] audit list` CLI command: + - [ ] List recent audit events + - [ ] Filter by event type, plan, date range + - [ ] Tests: Audit logging tests + - [ ] **SEC7.4** [Rui] Write Behave scenarios in `features/security_audit.feature`: + - [ ] Scenario: Apply creates audit log entry + - [ ] Scenario: Audit log queryable by plan ID + - [ ] Scenario: Audit list CLI shows recent events + +--- ### Section 12: Session & Provider Fixes [WORKSTREAM G - Hamza] **Target: Days 8-12** -**Parallel Group SESS1: Session Management [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: SESS1.session) - Commit message: "feat(session): add session model and CLI"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement Session model (session_id ULID, actor_name, title, created_at, updated_at) and persistence table `sessions`. - - [ ] Code [Hamza]: Implement SessionService with create/list/show/delete/export/import/tell operations per spec. - - [ ] Code [Hamza]: Implement CLI commands `session create/list/show/delete/export/import/tell` with rich/plain/json output. - - [ ] Docs [Hamza]: Add `docs/reference/session_management.md` with CLI examples and output fields. - - [ ] Tests (Behave) [Hamza]: Add `features/session_management.feature` scenarios for create/list/show/delete/export/import/tell. - - [ ] Tests (Robot) [Hamza]: Add session CLI smoke tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/session_cli_bench.py` for session command overhead. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(session): add session model and CLI"`. +- [ ] **Stage SESS1: Session Management** (Day 8-9) **[Hamza]** + - [ ] Code: Implement stable session persistence + - [ ] **SESS1.1** [Hamza] Define `Session` model in `src/cleveragents/domain/models/core/session.py`: + - [ ] Field `session_id: str` - ULID identifier + - [ ] Field `user_id: str | None` - optional user identity + - [ ] Field `automation_level: AutomationLevel` - session-level setting + - [ ] Field `current_plan_id: str | None` - active plan + - [ ] Field `plan_history: list[str]` - recent plan IDs + - [ ] Field `created_at: datetime` + - [ ] Field `last_active_at: datetime` + - [ ] Field `metadata: dict[str, Any]` - extensible metadata + - [ ] **SESS1.2** [Hamza] Create `SessionService` in `src/cleveragents/application/services/session_service.py`: + - [ ] Method `create_session() -> Session` - create new session with ULID + - [ ] Method `get_session(session_id: str) -> Session | None` + - [ ] Method `get_or_create_session() -> Session` - resume or create + - [ ] Method `update_activity(session_id: str) -> None` - touch last_active_at + - [ ] Method `set_current_plan(session_id: str, plan_id: str) -> None` + - [ ] Method `set_automation_level(session_id: str, level: AutomationLevel) -> None` + - [ ] **SESS1.3** [Hamza] Create Alembic migration for `sessions` table: + - [ ] Schema matching Session model + - [ ] Index on `last_active_at` for cleanup queries + - [ ] **SESS1.4** [Hamza] Implement session persistence across CLI invocations: + - [ ] Store session ID in `~/.cleveragents/session` + - [ ] Resume session on CLI startup if exists + - [ ] Clear session file on `agents [--data-dir PATH] [--config-path PATH] session end` command + - [ ] **SESS1.5** [Hamza] Add session CLI commands: + - [ ] `agents [--data-dir PATH] [--config-path PATH] session start` - create new session explicitly + - [ ] `agents [--data-dir PATH] [--config-path PATH] session end` - end current session + - [ ] `agents [--data-dir PATH] [--config-path PATH] session set automation-level ` - set session automation + - [ ] `agents [--data-dir PATH] [--config-path PATH] session info` - show current session details + - [ ] `agents [--data-dir PATH] [--config-path PATH] session tell "" [--session ] [--actor ]` - send a prompt in the active session + - [ ] Tests: Session tests + - [ ] **SESS1.6** [Rui] Write Behave scenarios in `features/session_management.feature`: + - [ ] Scenario: Session persists across CLI invocations + - [ ] Scenario: Session automation level overrides global + - [ ] Scenario: Session end clears session state + - [ ] Scenario: New CLI invocation resumes existing session + - [ ] Scenario: `agents [--data-dir PATH] [--config-path PATH] session tell "..."` uses current session and actor -**Parallel Group SESS2: Memory Persistence [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: SESS2.memory) - Commit message: "feat(memory): persist session history"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Persist MemoryService history keyed by session_id with backend config and retention limits. - - [ ] Code [Hamza]: Add `session_messages` table (session_id, role, content, created_at) and indexing for recent retrieval. - - [ ] Docs [Hamza]: Document memory backend options, retention policy, and export/import behavior. - - [ ] Tests (Behave) [Hamza]: Add `features/memory_persistence.feature` scenarios for save/load/trim. - - [ ] Tests (Robot) [Hamza]: Add memory persistence integration tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/memory_persistence_bench.py` for storage overhead baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(memory): persist session history"`. +- [ ] **Stage SESS2: Memory Service Persistence** (Day 9-10) **[Hamza]** + - [ ] Code: Fix memory loss between invocations + - [ ] **SESS2.1** [Hamza] Update `MemoryService` to use session-based storage: + - [ ] Store conversation history keyed by session_id + - [ ] Load history on session resume + - [ ] Support configurable history limits + - [ ] **SESS2.2** [Hamza] Create Alembic migration for `conversation_history` table: + - [ ] Schema: `history_id`, `session_id`, `plan_id`, `role`, `content`, `created_at` + - [ ] Foreign key to sessions + - [ ] Index on `session_id`, `plan_id` + - [ ] **SESS2.3** [Hamza] Add explicit memory configuration: + - [ ] `CLEVERAGENTS_MEMORY_BACKEND=sqlite|redis|memory` + - [ ] Document that `memory` backend loses history between invocations + - [ ] Default to `sqlite` for persistence + - [ ] **SESS2.4** [Hamza] Surface warning if memory not persistent: + - [ ] On first CLI invocation, warn if memory backend is `memory` + - [ ] Suggest configuring persistent backend + - [ ] Tests: Memory persistence tests + - [ ] **SESS2.5** [Rui] Write Behave scenarios in `features/memory_persistence.feature`: + - [ ] Scenario: Conversation history survives CLI restart + - [ ] Scenario: Memory backend warning shown for in-memory mode + - [ ] Scenario: Plan-specific memory isolated from other plans -**Parallel Group PROV1: Provider Fixes [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: PROV1.fixes) - Commit message: "fix(provider): remove FakeListLLM defaults"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Remove FakeListLLM fallback, fix auto-debug provider usage, and implement provider auto-detection. - - [ ] Code [Luis]: Update settings validation to fail fast when no providers are configured and no mock flag is set. - - [ ] Docs [Luis]: Update provider configuration docs and error messages. - - [ ] Tests (Behave) [Luis]: Add `features/provider_fixes.feature` scenarios. - - [ ] Tests (Robot) [Luis]: Add provider detection smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/provider_selection_bench.py` for provider resolution baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "fix(provider): remove FakeListLLM defaults"`. +- [ ] **Stage PROV1: Provider Fixes** (Day 10-11) **[Luis]** + - [ ] Code: Fix provider issues + - [ ] **PROV1.1** [Luis] Remove FakeListLLM as default behavior: + - [ ] Audit where FakeListLLM is used outside tests + - [ ] Ensure production code never falls back to FakeListLLM + - [ ] If no provider configured, fail fast with clear message: + ``` + Error: No LLM provider configured. + Set OPENAI_API_KEY, ANTHROPIC_API_KEY, or configure an actor. + See: agents [--data-dir PATH] [--config-path PATH] help providers + ``` + - [ ] **PROV1.2** [Luis] Fix auto-debug hardcoded provider: + - [ ] Auto-debug currently hardcoded to OpenAI GPT-4 + - [ ] Change to use configured default actor + - [ ] Or: use action/actor system (auto-debug is just an action) + - [ ] **PROV1.3** [Luis] Verify OpenRouter implementation: + - [ ] OpenRouter listed in spec but may not be fully implemented + - [ ] Test OpenRouter adapter with real API + - [ ] Fix any issues found + - [ ] **PROV1.4** [Luis] Implement provider auto-detection: + - [ ] On startup, detect which API keys are configured + - [ ] Register only available providers + - [ ] Clear message about which providers are available: + ``` + Available providers: openai (gpt-4, gpt-3.5-turbo), anthropic (claude-3-opus) + Missing: google (GEMINI_API_KEY not set) + ``` + - [ ] Tests: Provider tests + - [ ] **PROV1.5** [Rui] Write Behave scenarios in `features/provider_fixes.feature`: + - [ ] Scenario: No provider configured produces clear error + - [ ] Scenario: Auto-debug uses configured actor not hardcoded + - [ ] Scenario: Provider detection shows available providers + - [ ] Scenario: FakeListLLM only used in test mode -**Parallel Group PROV2: Cost Controls & Fallback [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: PROV2.costs) - Commit message: "feat(provider): add cost controls and fallback"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Track tokens/costs, enforce budgets, rate limits, and provider fallback order. - - [ ] Code [Luis]: Add cost tracking fields to plan execution metadata and surface in `plan status`. - - [ ] Docs [Luis]: Add `docs/reference/cost_controls.md` with config keys and thresholds. - - [ ] Tests (Behave) [Luis]: Add `features/cost_controls.feature` scenarios. - - [ ] Tests (Robot) [Luis]: Add cost control integration smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/cost_controls_bench.py` for cost check overhead. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(provider): add cost controls and fallback"`. +- [ ] **Stage PROV2: Provider Fallback & Cost Controls** (Day 11-12) **[Luis]** + - [ ] Code: Implement cost controls and fallback + - [ ] **PROV2.1** [Luis] Add token tracking per plan: + - [ ] Track input tokens, output tokens per LLM call + - [ ] Aggregate by plan, phase, actor + - [ ] Store in plan metadata + - [ ] **PROV2.2** [Luis] Add cost estimation: + - [ ] Map model to token cost ($ per 1K tokens) + - [ ] Calculate estimated cost per call + - [ ] Aggregate plan total cost + - [ ] **PROV2.3** [Luis] Implement budget limits: + - [ ] Per-plan max cost limit + - [ ] Per-session max cost limit + - [ ] Global max cost limit + - [ ] Warn when approaching limit, fail when exceeded + - [ ] **PROV2.4** [Luis] Implement rate limiting: + - [ ] Per-actor max calls per minute + - [ ] Per-plan max retries + - [ ] Exponential backoff on rate limit errors + - [ ] **PROV2.5** [Luis] Implement provider fallback: + - [ ] If primary provider fails, try fallback provider + - [ ] Configurable fallback order + - [ ] Log fallback events + - [ ] Tests: Cost control tests + - [ ] **PROV2.6** [Rui] Write Behave scenarios in `features/cost_controls.feature`: + - [ ] Scenario: Plan cost tracked and reported + - [ ] Scenario: Plan exceeding budget limit fails + - [ ] Scenario: Rate limit triggers exponential backoff + - [ ] Scenario: Provider fallback on transient failure --- ### Section 13: Additional CLI Commands & UX [Days 10-14] -**Parallel Group CLI0: Core System Commands [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: CLI0.core) - Commit message: "feat(cli): add version/info/diagnostics"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement `version`, `info`, and `diagnostics` commands with rich/plain/json/yaml output parity. - - [ ] Code [Hamza]: Add diagnostics checks for config file, database, providers, and filesystem permissions per spec. - - [ ] Docs [Hamza]: Update CLI reference with core system commands and sample outputs. - - [ ] Tests (Behave) [Hamza]: Add `features/cli_core.feature` scenarios for each command output. - - [ ] Tests (Robot) [Hamza]: Add core command smoke tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/cli_core_bench.py` for command runtime baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(cli): add version/info/diagnostics"`. +**Commands missing from initial plan** -**Parallel Group CLI1: Plan Interaction Commands [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: CLI1.plan) - Commit message: "feat(cli): add plan prompt/diff/artifacts"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement `plan prompt`, `plan diff`, and `plan artifacts` commands. - - [ ] Code [Hamza]: Ensure `plan diff` supports `--format` output and includes validation summary. - - [ ] Docs [Hamza]: Update CLI reference with plan interaction commands and output formats. - - [ ] Tests (Behave) [Hamza]: Add `features/plan_interaction_cli.feature` scenarios. - - [ ] Tests (Robot) [Hamza]: Add plan interaction CLI smoke tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/plan_cli_interaction_bench.py` for diff/artifacts runtime. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(cli): add plan prompt/diff/artifacts"`. +- [ ] **Stage CLI0: Core System Commands** (Day 10) **[Hamza]** + - [ ] Code: Implement core CLI metadata commands + - [ ] **CLI0.1** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] version`: + - [ ] Print CLI version and build metadata + - [ ] Include config path and data dir in verbose output + - [ ] **CLI0.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] info`: + - [ ] Show configuration summary (data dir, config path, providers, defaults) + - [ ] Show current session if present + - [ ] **CLI0.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] diagnostics`: + - [ ] Run self-checks (config readable, DB reachable, write access) + - [ ] Print warnings for missing providers or invalid config + - [ ] Tests: Core CLI command tests + - [ ] **CLI0.4** [Rui] Write Behave scenarios in `features/cli_core.feature`: + - [ ] Scenario: Version prints semantic version + - [ ] Scenario: Info prints config and data paths + - [ ] Scenario: Diagnostics reports missing providers -**Parallel Group CLI2: Configuration Commands [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: CLI2.config) - Commit message: "feat(cli): add config and provider commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement `config set/get/list` and `providers list` commands. - - [ ] Code [Hamza]: Support `config list` regex filtering and `--filter-values` per spec. - - [ ] Docs [Hamza]: Update CLI reference with configuration commands and filtering examples. - - [ ] Tests (Behave) [Hamza]: Add `features/config_cli.feature` scenarios. - - [ ] Tests (Robot) [Hamza]: Add config CLI smoke tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/config_cli_bench.py` for command parsing baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(cli): add config and provider commands"`. +- [ ] **Stage CLI1: Plan Interaction Commands** (Day 10-11) **[Hamza]** + - [ ] Code: Implement additional plan commands + - [ ] **CLI1.1** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan prompt ""`: + - [ ] Provide additional instructions to a stuck plan + - [ ] Works when plan is in errored state + - [ ] Resumes execution with new guidance + - [ ] **CLI1.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan diff `: + - [ ] Show diff of changes made by plan + - [ ] Works for plans in Execute or Apply phase + - [ ] Color-coded unified diff output + - [ ] **CLI1.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan diff --correction `: + - [ ] Compare old vs new after correction + - [ ] Show what changed between correction attempts + - [ ] **CLI1.4** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan artifacts `: + - [ ] List all artifacts produced by plan + - [ ] Show file paths, operation types, sizes + - [ ] Tests: Plan interaction CLI tests + - [ ] **CLI1.5** [Rui] Write Behave scenarios in `features/plan_interaction_cli.feature`: + - [ ] Scenario: Plan prompt resumes stuck plan + - [ ] Scenario: Plan diff shows color-coded output + - [ ] Scenario: Correction diff comparison works -**Parallel Group CLI3: Context Commands [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: CLI3.context) - Commit message: "feat(cli): add context policy commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement `project context set/show` and `actor context set/show` commands. - - [ ] Code [Hamza]: Support include/exclude resource and path globs, token limits, and summarize flags per spec. - - [ ] Docs [Hamza]: Update CLI reference with context policy usage and examples. - - [ ] Tests (Behave) [Hamza]: Add `features/context_cli.feature` scenarios. - - [ ] Tests (Robot) [Hamza]: Add context CLI smoke tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/context_cli_bench.py` for command runtime baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(cli): add context policy commands"`. +- [ ] **Stage CLI2: Configuration Commands** (Day 11-12) **[Hamza]** + - [ ] Code: Implement config commands + - [ ] **CLI2.1** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] config set `: + - [ ] Set global configuration values + - [ ] Supported keys: `automation-level`, `default-actor`, `log-level` + - [ ] Persist to `~/.cleveragents/config.toml` + - [ ] **CLI2.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] config get `: + - [ ] Get current configuration value + - [ ] Show source (default, file, environment) + - [ ] **CLI2.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] config list`: + - [ ] List all configuration values + - [ ] Show current value and source + - [ ] **CLI2.4** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] providers list`: + - [ ] List available providers + - [ ] Show which are configured vs missing API keys + - [ ] Tests: Config CLI tests + - [ ] **CLI2.5** [Rui] Write Behave scenarios in `features/config_cli.feature`: + - [ ] Scenario: Config set persists value + - [ ] Scenario: Config get shows current value + - [ ] Scenario: Providers list shows available/missing + +- [ ] **Stage CLI3: Context Commands** (Day 12-13) **[Hamza]** + - [ ] Code: Implement project/actor context policy commands + - [ ] **CLI3.1** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project context set`: + - [ ] Flags: `--project `, `--policy hot|warm|cold`, `--hot-max-tokens ` + - [ ] `--hot-max-tokens` is a soft cap; allow null/omitted to disable + - [ ] LLM hard context limit can override soft cap + - [ ] **CLI3.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project context show`: + - [ ] Display current policy and hot/warm/cold sizing + - [ ] **CLI3.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] actor context set`: + - [ ] Reuse same arguments/behavior as project context set and legacy context commands + - [ ] **CLI3.4** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] actor context show`: + - [ ] Reuse same output format as project context show + - [ ] Tests: Context command tests + - [ ] **CLI3.5** [Rui] Write Behave scenarios in `features/context_cli.feature`: + - [ ] Scenario: Project context set updates policy + - [ ] Scenario: Project context show displays hot/warm/cold tiers + - [ ] Scenario: Actor context set mirrors project context args + - [ ] Scenario: Actor context show displays policy summary --- ### Section 14: Concurrency & Cleanup [Days 12-14] -**Parallel Group CONC1: Plan Locking [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: CONC1.lock) - Commit message: "feat(concurrency): add plan and project locks"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Implement plan-level and project-level locks with timeouts. - - [ ] Code [Luis]: Add `locks` table with owner_id, resource_type, resource_id, acquired_at, expires_at. - - [ ] Code [Luis]: Ensure locks are enforced in PlanLifecycleService transitions and SubplanService scheduling. - - [ ] Docs [Luis]: Add `docs/reference/concurrency.md` with lock behavior. - - [ ] Tests (Behave) [Luis]: Add `features/concurrency.feature` scenarios for lock contention and expiry. - - [ ] Tests (Robot) [Luis]: Add lock integration smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/concurrency_lock_bench.py` for lock overhead baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(concurrency): add plan and project locks"`. +- [ ] **Stage CONC1: Plan Locking** (Day 12) **[Luis]** + - [ ] Code: Prevent concurrent plan modification + - [ ] **CONC1.1** [Luis] Implement plan-level locking: + - [ ] Database row lock on plan during execution + - [ ] Or: advisory lock using plan_id + - [ ] Prevent two processes from executing same plan + - [ ] **CONC1.2** [Luis] Implement project-level locking: + - [ ] Lock project during apply (can't apply two plans to same project) + - [ ] Allow parallel plans in different sandboxes + - [ ] **CONC1.3** [Luis] Add lock timeout and retry: + - [ ] Configurable lock wait timeout + - [ ] Clear error if lock cannot be acquired + - [ ] Tests: Concurrency tests + - [ ] **CONC1.4** [Rui] Write Behave scenarios in `features/concurrency.feature`: + - [ ] Scenario: Two processes cannot execute same plan + - [ ] Scenario: Two applies to same project blocked + - [ ] Scenario: Lock timeout produces clear error -**Parallel Group CONC2: Resumable Execution [Luis]** -- [ ] **COMMIT (Owner: Luis | Group: CONC2.resume) - Commit message: "feat(concurrency): add plan resume"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Persist step-level progress and implement `plan resume` with graceful shutdown handling. - - [ ] Code [Luis]: Add resume checkpoints tied to decision IDs and sandbox checkpoints. - - [ ] Docs [Luis]: Update plan lifecycle docs for resume behavior. - - [ ] Tests (Behave) [Luis]: Add `features/plan_resume.feature` scenarios. - - [ ] Tests (Robot) [Luis]: Add resume integration tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/plan_resume_bench.py` for resume overhead baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(concurrency): add plan resume"`. +- [ ] **Stage CONC2: Resumable Execution** (Day 12-13) **[Luis]** + - [ ] Code: Implement plan resume capability + - [ ] **CONC2.1** [Luis] Persist step-level progress: + - [ ] Record each completed step in plan + - [ ] Store intermediate state for resume + - [ ] **CONC2.2** [Luis] Implement `agents [--data-dir PATH] [--config-path PATH] plan resume `: + - [ ] Detect where plan was interrupted + - [ ] Resume from last completed step + - [ ] Restore sandbox state + - [ ] **CONC2.3** [Luis] Handle graceful shutdown: + - [ ] On SIGINT/SIGTERM, save progress before exit + - [ ] Mark plan as "interrupted" not "errored" + - [ ] Tests: Resume tests + - [ ] **CONC2.4** [Rui] Write Behave scenarios in `features/plan_resume.feature`: + - [ ] Scenario: Interrupted plan can be resumed + - [ ] Scenario: Resume continues from correct step + - [ ] Scenario: Graceful shutdown saves progress -**Parallel Group CONC3: Garbage Collection [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: CONC3.gc) - Commit message: "feat(ops): add cleanup commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Add cleanup for sandboxes, checkpoints, and stale sessions with CLI commands. - - [ ] Code [Hamza]: Add retention policy settings for sandbox age, checkpoint count, and session inactivity. - - [ ] Docs [Hamza]: Document cleanup commands and retention defaults. - - [ ] Tests (Behave) [Hamza]: Add `features/garbage_collection.feature` scenarios. - - [ ] Tests (Robot) [Hamza]: Add cleanup integration smoke tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/cleanup_bench.py` for cleanup overhead baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(ops): add cleanup commands"`. +- [ ] **Stage CONC3: Garbage Collection** (Day 13-14) **[Hamza]** + - [ ] Code: Cleanup abandoned resources + - [ ] **CONC3.1** [Hamza] Implement sandbox garbage collection: + - [ ] On startup, find orphaned sandbox directories + - [ ] Clean sandboxes from crashed processes + - [ ] Add `agents [--data-dir PATH] [--config-path PATH] cleanup sandboxes` CLI command + - [ ] **CONC3.2** [Hamza] Implement checkpoint file cleanup: + - [ ] Track checkpoint files in database + - [ ] Retention policy (default: 7 days after plan completion) + - [ ] Add `agents [--data-dir PATH] [--config-path PATH] cleanup checkpoints` CLI command + - [ ] **CONC3.3** [Hamza] Implement session cleanup: + - [ ] Clean sessions with no activity for 30 days + - [ ] Clean associated conversation history + - [ ] **CONC3.4** [Hamza] Add automatic cleanup on startup: + - [ ] Run cleanup for orphaned resources + - [ ] Log what was cleaned + - [ ] **CONC3.5** [Brent] Review resource lifecycle patterns only: + - [ ] Validate cleanup doesn't affect active resources + - [ ] Review concurrent access during cleanup + - [ ] Tests: Cleanup tests + - [ ] **CONC3.6** [Rui] Write Behave scenarios in `features/garbage_collection.feature`: + - [ ] Scenario: Orphaned sandbox cleaned on startup + - [ ] Scenario: Old checkpoints cleaned by retention policy + - [ ] Scenario: Cleanup commands work manually + +--- + +### Section 15: Definition of Done & Invariants [Days 14-15] + +- [ ] **Stage DOD1: Definition of Done Enforcement** (Day 14) **[Luis]** + - [ ] Code: Implement DoD validation + - [ ] **DOD1.1** [Luis] Parse DoD must/should/may structure: + - [ ] Parse action's `definition_of_done` field + - [ ] Identify MUST, SHOULD, MAY requirements + - [ ] Generate validation checklist from DoD + - [ ] **DOD1.2** [Luis] Validate DoD after execution: + - [ ] Run DoD checklist after Execute completes + - [ ] MUST requirements block apply if failed + - [ ] SHOULD requirements warn but allow apply + - [ ] MAY requirements are informational only + - [ ] **DOD1.3** [Luis] Display DoD validation results: + - [ ] Show checklist in CLI output + - [ ] Color-code: green (pass), red (fail), yellow (warn) + - [ ] Tests: DoD tests + - [ ] **DOD1.4** [Rui] Write Behave scenarios in `features/definition_of_done.feature`: + - [ ] Scenario: DoD MUST failure blocks apply + - [ ] Scenario: DoD SHOULD failure warns but allows apply + - [ ] Scenario: DoD validation results displayed + +- [ ] **Stage DOD2: Invariant System** (Day 14-15) **[Luis]** + - [ ] Code: Implement user-defined invariants + - [ ] **DOD2.1** [Luis] Define `Invariant` model: + - [ ] Field `invariant_id: str` + - [ ] Field `project_id: str` + - [ ] Field `description: str` - human readable + - [ ] Field `check_command: str | None` - shell command to verify + - [ ] Field `check_code: str | None` - Python code to verify + - [ ] Field `severity: str` - error|warning + - [ ] **DOD2.2** [Luis] Implement `agents [--data-dir PATH] [--config-path PATH] project add-invariant`: + - [ ] Add invariant to project + - [ ] Specify check command or code + - [ ] **DOD2.3** [Luis] Check invariants during execution: + - [ ] Run invariant checks after each major step + - [ ] Fail execution if invariant violated (severity=error) + - [ ] Warn if invariant violated (severity=warning) + - [ ] Tests: Invariant tests + - [ ] **DOD2.4** [Rui] Write Behave scenarios in `features/invariants.feature`: + - [ ] Scenario: Invariant violation blocks execution + - [ ] Scenario: Invariant warning allows continuation + - [ ] Scenario: Invariant with command check works --- ### Section 16: Context Indexing [Days 15-17] -**Parallel Group CTX1: Repository Indexing [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: CTX1.index) - Commit message: "feat(context): add repo indexing service"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Implement indexing service with file tree, language detection, and incremental refresh. - - [ ] Code [Hamza]: Add index metadata table (resource_id, indexed_at, file_count, token_estimate). - - [ ] Code [Hamza]: Enforce max file size and total size limits from project context policy. - - [ ] Docs [Hamza]: Add `docs/reference/context_indexing.md`. - - [ ] Tests (Behave) [Hamza]: Add `features/context_indexing.feature` scenarios. - - [ ] Tests (Robot) [Hamza]: Add indexing integration tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/context_indexing_bench.py` for indexing throughput baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(context): add repo indexing service"`. +- [ ] **Stage CTX1: Repository Indexing** (Day 15-16) **[Hamza]** + - [ ] Code: Implement repo indexing for large codebases + - [ ] **CTX1.1** [Hamza] Create `IndexingService` in `src/cleveragents/application/services/indexing_service.py`: + - [ ] Method `index_project(project: Project) -> Index`: + - [ ] Scan all files in project resources + - [ ] Apply ignore patterns + - [ ] Build file tree index + - [ ] Detect language per file + - [ ] Method `search(query: str, project_id: str) -> list[SearchResult]`: + - [ ] Full-text search across indexed files + - [ ] Return file paths, line numbers, snippets + - [ ] Method `refresh_index(project_id: str) -> None`: + - [ ] Update index for changed files only + - [ ] **CTX1.2** [Hamza] Implement file tree representation: + - [ ] Parse directory structure + - [ ] Include file sizes, modification times + - [ ] Support efficient subtree queries + - [ ] **CTX1.3** [Hamza] Add language detection: + - [ ] Detect language from file extension + - [ ] Detect language from shebang/magic bytes + - [ ] Store language in index + - [ ] Tests: Indexing tests + - [ ] **CTX1.4** [Rui] Write Behave scenarios in `features/context_indexing.feature`: + - [ ] Scenario: Project indexing creates searchable index + - [ ] Scenario: Search returns relevant results + - [ ] Scenario: Index refresh updates changed files only -**Parallel Group CTX2: Embedding Index [Hamza]** -- [ ] **COMMIT (Owner: Hamza | Group: CTX2.embedding) - Commit message: "feat(context): add optional embedding search"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Add embedding-based search with opt-in flag and fallback to full-text search. - - [ ] Code [Hamza]: Add embedding index metadata and cache invalidation on repo updates. - - [ ] Docs [Hamza]: Add `docs/reference/embedding_search.md`. - - [ ] Tests (Behave) [Hamza]: Add `features/embedding_search.feature` scenarios. - - [ ] Tests (Robot) [Hamza]: Add embedding search integration tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/embedding_search_bench.py` for search runtime baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(context): add optional embedding search"`. +- [ ] **Stage CTX2: Embedding Index** (Day 16-17) **[Hamza]** + - [ ] Code: Optional embedding-based search + - [ ] **CTX2.1** [Hamza] Integrate with VectorStoreService: + - [ ] Chunk files into segments + - [ ] Generate embeddings for each chunk + - [ ] Store in FAISS index + - [ ] **CTX2.2** [Hamza] Implement semantic search: + - [ ] Method `semantic_search(query: str, project_id: str) -> list[SearchResult]` + - [ ] Use query embedding to find similar chunks + - [ ] Return ranked results with relevance scores + - [ ] **CTX2.3** [Hamza] Make embedding index optional: + - [ ] Only build if `CLEVERAGENTS_ENABLE_EMBEDDINGS=true` + - [ ] Fall back to full-text search if not available + - [ ] Tests: Embedding tests + - [ ] **CTX2.4** [Rui] Write Behave scenarios in `features/embedding_search.feature`: + - [ ] Scenario: Semantic search returns conceptually similar results + - [ ] Scenario: System works without embedding index --- ### Section 17: Skill Registry [Days 17-18] +- [ ] **Stage SKILL1: Skill Catalog** (Day 17-18) **[Aditya]** + - [ ] Code: Implement skill registry for safety validation + - [ ] **SKILL1.1** [Aditya] Create `SkillRegistry` in `src/cleveragents/actor/skills/registry.py`: + - [ ] Method `register_skill(skill: Skill) -> None` + - [ ] Method `get_skill(name: str) -> Skill | None` + - [ ] Method `list_skills() -> list[Skill]` + - [ ] Method `list_skills_by_capability(read_only: bool) -> list[Skill]` + - [ ] **SKILL1.2** [Aditya] Auto-register skills from actor configs: + - [ ] When actor config parsed, extract tool definitions + - [ ] Register each tool as a skill with metadata + - [ ] **SKILL1.3** [Aditya] Validate skill usage against plan requirements: + - [ ] Check skill capabilities against action requirements + - [ ] Fail if incompatible skill used + - [ ] **SKILL1.4** [Aditya] Implement `agents [--data-dir PATH] [--config-path PATH] skills list`: + - [ ] List all registered skills + - [ ] Show capabilities (read_only, checkpointable, etc.) + - [ ] Tests: Skill registry tests + - [ ] **SKILL1.5** [Rui] Write Behave scenarios in `features/skill_registry.feature`: + - [ ] Scenario: Skills auto-registered from actor config + - [ ] Scenario: Skill capability query works + - [ ] Scenario: Incompatible skill usage detected + --- ### Section 18: Deferred Work -Deferred items remain planned but are not part of the 30-day MVP scope. +The following items are deferred or no longer applicable: -- [ ] **COMMIT (Owner: Hamza | Group: POST.resource) - Commit message: "feat(resource): add virtual resource equivalence tracking"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Hamza]: Add `virtual_resource_links` table mapping virtual resource ULID to physical resource ULIDs with uniqueness constraints. - - [ ] Code [Hamza]: Add `ResourceEquivalenceService` to create/merge virtual resources and update links on content divergence. - - [ ] Code [Hamza]: Add helper to compute equivalence key (hash or name) for auto-linking during resource discovery. - - [ ] Docs [Hamza]: Update `docs/reference/resource_model.md` with physical/virtual equivalence rules and examples. - - [ ] Tests (Behave) [Hamza]: Add scenarios for linking/unlinking physical resources to virtual resources and divergence updates. - - [ ] Tests (Robot) [Hamza]: Add Robot test that creates two identical physical resources and verifies a shared virtual resource. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/virtual_resource_bench.py` for equivalence update overhead. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(resource): add virtual resource equivalence tracking"`. - -- [ ] **COMMIT (Owner: Luis | Group: POST.server) - Commit message: "feat(client): add server http client"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add HTTP client with health check, version negotiation, and OpenAPI codegen integration. - - [ ] Code [Luis]: Add config keys for server base URL, API token, and TLS verification; wire into Settings. - - [ ] Code [Luis]: Map server error responses into domain errors with retry hints. - - [ ] Docs [Luis]: Add `docs/reference/server_client_http.md` with configuration and connection errors. - - [ ] Tests (Behave) [Luis]: Add scenarios for connection errors and version mismatch handling. - - [ ] Tests (Robot) [Luis]: Add mock-server connection tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/server_http_client_bench.py` for connection overhead baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(client): add server http client"`. - -- [ ] **COMMIT (Owner: Luis | Group: POST.server) - Commit message: "feat(client): add plan sync and remote execution"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Sync actions, request remote plan execution/apply/status, and reconcile remote plan IDs. - - [ ] Code [Luis]: Add conflict resolution policy (local wins vs server wins) with explicit CLI errors on ambiguity. - - [ ] Docs [Luis]: Document sync semantics and conflict handling in `docs/reference/server_sync.md`. - - [ ] Tests (Behave) [Luis]: Add scenarios for sync conflicts and retry behavior. - - [ ] Tests (Robot) [Luis]: Add mock-server sync tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/server_sync_bench.py` for sync throughput baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(client): add plan sync and remote execution"`. - -- [ ] **COMMIT (Owner: Luis | Group: POST.server) - Commit message: "feat(client): add websocket updates"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add WebSocket client for plan updates with reconnect/backoff policy. - - [ ] Code [Luis]: Define event schema mapping for plan status/progress/log stream updates. - - [ ] Docs [Luis]: Add `docs/reference/server_websocket.md` with event types and reconnect rules. - - [ ] Tests (Behave) [Luis]: Add scenarios for reconnect and event ordering. - - [ ] Tests (Robot) [Luis]: Add WebSocket mock tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/server_ws_bench.py` for message handling baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(client): add websocket updates"`. - -- [ ] **COMMIT (Owner: Hamza | Group: POST.server) - Commit message: "feat(client): add remote project support"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Hamza]: Add remote resource selection and server execution request wiring. - - [ ] Code [Hamza]: Add project-name resolution rules for remote namespaces and server aliases. - - [ ] Docs [Hamza]: Add `docs/reference/server_remote_projects.md` with project selection semantics. - - [ ] Tests (Behave) [Hamza]: Add scenarios for remote project selection errors. - - [ ] Tests (Robot) [Hamza]: Add remote execution mock tests. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/server_remote_project_bench.py` for request overhead baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(client): add remote project support"`. - -- [ ] **COMMIT (Owner: Rui | Group: POST.repl) - Commit message: "feat(cli): add interactive repl"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Rui]: Implement `agents repl` command that dispatches to existing CLI commands with shared config handling. - - [ ] Code [Rui]: Add history support with opt-out (`--no-history`) and default path `~/.cleveragents/history`. - - [ ] Code [Rui]: Add tab-completion for top-level commands and last command repetition (`!!`). - - [ ] Docs [Rui]: Add REPL usage guide with supported commands and exit behavior. - - [ ] Tests (Behave) [Rui]: Add REPL behavior scenarios (history on/off, unknown command, exit). - - [ ] Tests (Robot) [Rui]: Add REPL smoke tests for command dispatch. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/repl_bench.py` for REPL startup baseline. - - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "feat(cli): add interactive repl"`. - -- [ ] **COMMIT (Owner: Luis | Group: POST.auth) - Commit message: "feat(cli): add auth and team commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add `agents auth login/logout/status` and `agents team list/use` commands with stubbed responses when server is disabled. - - [ ] Code [Luis]: Add config keys for auth token storage, active team, and default namespace (client-only stubs). - - [ ] Code [Luis]: Wire stubbed commands to `AuthClient` and `ServerClient` interfaces (raise NotImplementedError when no server). - - [ ] Docs [Luis]: Document auth/team workflows and local-only stub behavior. - - [ ] Tests (Behave) [Luis]: Add auth/team CLI scenarios (stubbed responses, missing server errors). - - [ ] Tests (Robot) [Luis]: Add auth/team integration smoke tests. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/auth_cli_bench.py` for auth command baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(cli): add auth and team commands"`. - -- [ ] **COMMIT (Owner: Jeff | Group: POST.tui) - Commit message: "feat(ui): add TUI/Web interface"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Define UI data-provider interface (plans, sessions, validations, diffs, logs) backed by local services. - - [ ] Code [Jeff]: Implement minimal TUI with plan list, plan detail, diff viewer, and validation summary panes. - - [ ] Code [Jeff]: Add Web UI stub that serves the same data via local-only routes (read-only by default). - - [ ] Docs [Jeff]: Add UI usage guide with navigation and data-refresh behavior. - - [ ] Tests (Behave) [Jeff]: Add UI behavior scenarios (list, detail, diff, refresh). - - [ ] Tests (Robot) [Jeff]: Add UI smoke tests for route loading and TUI navigation. - - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/ui_render_bench.py` for UI render baseline. - - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(ui): add TUI/Web interface"`. - -- [ ] **COMMIT (Owner: Hamza | Group: POST.dbresources) - Commit message: "feat(resource): add database resources"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Hamza]: Add database resource types (postgres, mysql, sqlite, duckdb) with connection args and auth handling. - - [ ] Code [Hamza]: Implement sandbox strategy using transaction wrappers and read-only toggles. - - [ ] Docs [Hamza]: Document database resource configuration and supported auth options. - - [ ] Tests (Behave) [Hamza]: Add database resource scenarios (connection validation, read-only enforcement). - - [ ] Tests (Robot) [Hamza]: Add database resource integration tests (local sqlite/duckdb only). - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/db_resource_bench.py` for resource registration baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(resource): add database resources"`. - -- [ ] **COMMIT (Owner: Hamza | Group: POST.cloud) - Commit message: "feat(resource): add cloud infrastructure resources"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Hamza]: Add cloud resource types (aws, gcp, azure) with credential fields and region/tenant metadata. - - [ ] Code [Hamza]: Add stubbed sandbox strategies that validate configuration and return NotImplementedError for execution. - - [ ] Docs [Hamza]: Document cloud resource configuration and local-only stub behavior. - - [ ] Tests (Behave) [Hamza]: Add cloud resource scenarios (schema validation, stub errors). - - [ ] Tests (Robot) [Hamza]: Add cloud resource integration tests with stubbed responses. - - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/cloud_resource_bench.py` for resource registration baseline. - - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Hamza]: `git commit -m "feat(resource): add cloud infrastructure resources"`. - -- [ ] **COMMIT (Owner: Luis | Group: POST.permissions) - Commit message: "feat(security): add permission system"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Implement namespace/project/plan/skill permission model (role bindings, default deny, allow overrides). - - [ ] Code [Luis]: Add enforcement hooks at CLI/service boundaries (server-only; local mode returns permissive defaults). - - [ ] Docs [Luis]: Document permission model, role matrix, and server-only behavior. - - [ ] Tests (Behave) [Luis]: Add permission scenarios (allow/deny, missing role, server disabled). - - [ ] Tests (Robot) [Luis]: Add permission integration tests with stubbed server client. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/permission_check_bench.py` for enforcement baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(security): add permission system"`. - -- [ ] **COMMIT (Owner: Luis | Group: POST.safety) - Commit message: "feat(security): add safety profile enforcement"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add SafetyProfile model, CLI flags, and execution enforcement hooks (server-only for now). - - [ ] Code [Luis]: Add safety profile resolution order (plan > project > global) with defaults. - - [ ] Docs [Luis]: Document safety profile options, defaults, and server-only behavior. - - [ ] Tests (Behave) [Luis]: Add safety profile enforcement scenarios (deny/allow paths). - - [ ] Tests (Robot) [Luis]: Add safety profile integration tests with stubbed server client. - - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/safety_profile_bench.py` for enforcement baseline. - - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(security): add safety profile enforcement"`. +- [ ] **Deferred: REPL Mode** - Focus on CLI first, REPL is optional enhancement +- [ ] **Deferred: Auth/Team Commands** - Requires server connectivity (server is a separate project) +- [ ] **Deferred: TUI/Web Interface** - After CLI is complete +- [ ] **Deferred: Database Resources** - After source code resources work +- [ ] **Deferred: Cloud Infrastructure Resources** - After source code resources work +- [ ] **Deferred: Permission System** - Requires server connectivity (server is a separate project) + - [ ] Namespace-level permissions (who can create/edit org actions) + - [ ] Project-level permissions (who can modify resources, apply changes) + - [ ] Plan-level permissions (can this plan write, require approvals) + - [ ] Skill-level permissions (require approval per call or elevated role) +- [ ] **POST1: Safety Profile Enforcement (Post-30)** - Deferred safety policy system + - [ ] Move `safety_profile` into Action model (from Stage A2 follow-up) + - [ ] Define `SafetyProfile` model with allow/deny skill categories and approval gates + - [ ] Add action-create CLI flags for safety profile: + - [ ] `--require-sandbox` + - [ ] `--require-checkpoints` + - [ ] `--require-apply-approval` + - [ ] `--allow-skill-category ` (repeatable) + - [ ] `--deny-skill-category ` (repeatable) + - [ ] `--max-cost-usd ` + - [ ] `--max-retries ` + - [ ] Enforce safety profile during execution and apply + - [ ] Add Behave + Robot tests for safety profile enforcement +- [ ] **Removed: Old 67-command structure** - Replaced by new command structure +- [ ] **Removed: Configuration migration utilities** - CleverAgents is standalone --- @@ -3111,81 +9376,81 @@ Deferred items remain planned but are not part of the 30-day MVP scope. ### TEAM ROLES AND ASSIGNMENTS -| Developer | Role | Primary Focus Areas | Notes | -|-----------|------|---------------------|-------| -| **Jeff** | CTO/Lead Architect | Critical path items, architectural decisions, complex integrations | Fastest, most expert developer - handles blocking issues | -| **Luis** | Senior Python Architect | Domain models, persistence, algorithms, state machines | Good architecture but can be pedantic - needs clear requirements | -| **Aditya** | Domain Expert (Agents/LLMs) | Actor YAML configs, hierarchical actors, skill execution | Understands topic best but code may need cleanup | -| **Hamza** | Python/RDF Expert | Resources, sandbox, database, general Python | Well-rounded, no agent experience - assign infrastructure | -| **Rui** | Fast Developer | Testing (Behave/Robot), simpler implementations | New to Python - assign testing and straightforward tasks | -| **Brent** | Quality Specialist | Code review, linting, type checking, documentation | Slow but detail-oriented - low contention independent work | -| **Mike/Brian** | Sysadmins | Deployment, infrastructure setup | Minimal coding tasks | +| Developer | Role | Primary Focus Areas | Notes | +| -------------- | --------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- | +| **Jeff** | CTO/Lead Architect | Critical path items, architectural decisions, complex integrations | Fastest, most expert developer - handles blocking issues | +| **Luis** | Senior Python Architect | Domain models, persistence, algorithms, state machines | Good architecture but can be pedantic - needs clear requirements | +| **Aditya** | Domain Expert (Agents/LLMs) | Actor YAML configs, hierarchical actors, skill execution | Understands topic best but code may need cleanup | +| **Hamza** | Python/RDF Expert | Resources, sandbox, database, general Python | Well-rounded, no agent experience - assign infrastructure | +| **Rui** | Fast Developer | Testing (Behave/Robot), simpler implementations | New to Python - assign testing and straightforward tasks | +| **Brent** | Quality Specialist | Code review, linting, type checking, documentation | Slow but detail-oriented - low contention independent work | +| **Mike/Brian** | Sysadmins | Deployment, infrastructure setup | Minimal coding tasks | ### Week 1 (Days 1-7) - MVP Target (Source Code Only) | Day | Morning Focus | Owner | Afternoon Focus | Owner | |-----|---------------|-------|-----------------|-------| -| 1 | A5.alpha + A5.action_arguments DB migrations + ORM models | Jeff + Luis | B1.core Project/Resource models | Hamza | -| 2 | A5.gamma repos/services + A5.tests + B3.cleanup (legacy project CLI) | Jeff + Rui (tests) | B2.persistence/B2.service + B3.cli Resource CLI | Hamza + Rui (tests) | -| 3 | B4.sandbox git_worktree | Jeff + Hamza | B4.sandbox manager + tests | Luis + Rui (tests) | -| 4 | C1.schema/C1.examples Actor YAML | Aditya | C2.loader/C2.compiler + C2.legacy v2 removal | Aditya + Jeff | -| 5 | C3.protocol/C3.context/C3.inline Skill framework | Jeff | C4.file/C4.search Built-in skills | Luis + Jeff | -| 6 | C7.mcp MCP Adapter | Aditya + Jeff | C4.git + C5.model/C5.router Change tracking | Luis + Jeff | -| 7 | C5.diff Diff review artifacts | Luis | C6.pipeline/C6.gating Validation pipeline | Luis + Rui (tests) | +| 1 | A5.1-A5.4 Plan/Action DB Schema | Jeff + Luis | B1.1-B1.6 Project/Resource Models | Hamza | +| 2 | A5.5-A5.9 Plan/Action Repositories | Jeff | B2.1-B2.4 Project CLI Commands | Hamza + Rui (tests) | +| 3 | B3.1-B3.6 Sandbox Protocol + Git | Jeff + Hamza | B3.7-B3.13 Sandbox Manager + Tests | Luis + Rui | +| 4 | C1.1-C1.6 Actor YAML Schema | Aditya | C2.1-C2.8 Actor Compiler | Aditya + Jeff | +| 5 | C3.1-C3.5 Skill Protocol | Jeff | C3.6 Built-in File Skills | Luis + Jeff | +| 6 | C3.7 MCP Adapter | Aditya + Jeff | C4.1-C4.3 Change Tracking | Luis | +| 7 | C4.4-C4.7 Tool Router + Diff | Jeff | C5.1-C5.5 Validation Pipeline | Luis + Rui | ### Week 2 (Days 8-14) - M3 Complete + Plan-Actor Integration | Day | Focus | Owner | Deliverable | |-----|-------|-------|-------------| -| 8 | C8.providers + C9.execute Plan-Actor integration | Jeff + Aditya (+Luis support) | Execute phase + providers ready | -| 9 | C9.apply Apply Phase + Review | Jeff + Luis | Apply flow ready with review gates | -| 10 | D1.domain Decision Model | Hamza (+Jeff review) | Decision model committed | -| 11 | D2.service Decision Recording | Hamza (+Jeff review) | Decision recording committed | -| 12 | E1.domain Subplan Model | Luis (+Jeff review) | Subplan domain committed | -| 13 | E2.service/E2.actor Subplan spawning | Jeff + Aditya (+Luis support) | Subplan spawn committed | -| 14 | End-to-end integration testing | All + Rui (tests) + Brent (QA) | M3 milestone verified | +| 8 | C6.1-C6.8 Plan-Actor Integration | Jeff + Aditya | Full execute phase working | +| 9 | C7.1-C7.6 Apply Phase + Diff Review | Jeff + Luis | Apply with review gates | +| 10 | D1.1-D1.8 Decision Model | Hamza + Jeff | Decision recording foundation | +| 11 | D2.1-D2.6 Decision Recording | Jeff + Hamza | Decisions captured in Strategize | +| 12 | E1.1-E1.6 Subplan Model | Luis | Subplan spawning design | +| 13 | E2.1-E2.5 Subplan Execution | Jeff + Luis | Sequential subplan execution | +| 14 | End-to-end integration testing | All + Rui | M3 milestone verified | ### Week 3 (Days 15-21) - M4 Target (Decision Tree + Correction) | Day | Focus | Owner | Deliverable | |-----|-------|-------|-------------| -| 15 | D5.db/D5.repo Decision persistence | Hamza (+Jeff review) | Decision storage wired | -| 16 | D3.cli Decision viewing | Hamza (+Jeff review) | `agents [--data-dir PATH] [--config-path PATH] plan tree`, `agents [--data-dir PATH] [--config-path PATH] plan explain` | -| 17 | D4.revert Decision correction (revert) | Jeff (+Luis support) | `agents [--data-dir PATH] [--config-path PATH] plan correct` revert flow | -| 18 | D4.append + D5.di Decision wiring | Jeff + Hamza (+Luis support) | Append correction + service wiring | -| 19 | E3.exec Parallel Subplan Execution | Luis (+Jeff review) | Concurrent subplans | -| 20 | E4.merge Result Merging | Jeff (+Luis support) | Git-style merge for subplans | -| 21 | M4 integration testing | All + Rui (tests) + Brent (QA) | Decision correction working | +| 15 | D3.1-D3.6 Decision Tree Storage | Hamza | Decision persistence | +| 16 | D4.1-D4.5 Decision CLI Commands | Hamza + Rui | `agents [--data-dir PATH] [--config-path PATH] plan tree`, `agents [--data-dir PATH] [--config-path PATH] plan explain` | +| 17 | D5.1-D5.8 Decision Correction | Jeff | `agents [--data-dir PATH] [--config-path PATH] plan correct` implementation | +| 18 | D5.9-D5.12 Replay Mechanism | Jeff + Luis | Downstream recomputation | +| 19 | E3.1-E3.5 Parallel Subplan Execution | Luis | Concurrent subplans | +| 20 | E4.1-E4.5 Result Merging | Jeff + Luis | Git-style merge for subplans | +| 21 | M4 integration testing | All | Decision correction working | ### Week 4 (Days 22-30) - M6 Target (Large Project Autonomy - LOCAL MODE ONLY) | Day | Focus | Owner | Deliverable | |-----|-------|-------|-------------| -| 22-23 | CTX1.index Context indexing + G3.semantic | Hamza + Luis | Large codebase indexing + semantic validation | -| 24-25 | G4.context Hot/Warm/Cold tiers + G2.checkpoint | Hamza + Luis | Three-tier memory + checkpointing | -| 26-27 | G1.decompose + G5.estimate | Jeff + Hamza | Autonomous decomposition + estimation | -| 28 | F0.stubs | Luis | Client stubs (NOT server impl) | -| 29 | M6 perf triage + large project tests | All + Rui (tests) + Brent (QA) | 10K file perf target | -| 30 | M6 integration testing | All + Rui (tests) + Brent (QA) | Large project autonomy verified (Server connectivity DEFERRED) | +| 22-23 | F1.1-F1.8 Context Indexing | Hamza | Large codebase indexing | +| 24-25 | F2.1-F2.6 Hot/Warm/Cold Context | Jeff + Hamza | Three-tier memory | +| 26-27 | Deep Subplan Hierarchies (5+ levels) | Jeff + Luis | Autonomous decomposition | +| 28-29 | F0.1-F0.5 Server Client Interface Stubs + Large Project Tests | Luis + Rui | Client stubs (NOT server impl), 10K file tests | +| 30 | M6 integration testing | All | Large project autonomy verified (Server connectivity DEFERRED) | > **Note**: Server connectivity (F1-F4) is DEFERRED beyond Day 30. Days 26-29 focus on client **stubs only** and large project testing. The server is a separate project. ### Week 5 (Days 31-35) - M7 Target (Server Connectivity - Client Side Only) | Day | Focus | Owner | Deliverable | |-----|-------|-------|-------------| -| 31-32 | F1.client Server client infrastructure | Luis (+Jeff review) | HTTP client for server communication | -| 33 | F2.sync Plan sync client | Luis (+Jeff review) | Client can sync plans to server | -| 34 | F3.ws WebSocket client | Luis (+Jeff review) | Client receives real-time updates | -| 35 | F4.remote Remote project support | Hamza (+Jeff review) | Client can request server execution | +| 31-32 | F1.1-F1.4 Server Client Infrastructure | Luis | HTTP client for server communication | +| 33 | F2.1-F2.6 Plan Sync Client | Luis | Client can sync plans to server | +| 34 | F3.1-F3.3 WebSocket Client | Luis | Client receives real-time updates | +| 35 | F4.1-F4.3 Remote Project Support | Hamza | Client can request server execution | ### Week 6 (Days 36-40) - M8 Target (Full Feature Set + Polish) | Day | Focus | Owner | Deliverable | |-----|-------|-------|-------------| -| 36 | A6.* automation level refinements | Jeff + Luis | Automation modes stabilized | -| 37 | G5.estimate cost/risk estimation | Hamza (+Jeff review) | Estimation working | -| 38 | G2.checkpoint rollback | Luis (+Jeff review) | Checkpointing + rollback | -| 39 | G1.decompose + G3.semantic performance tuning | Jeff + Luis | Benchmarks passing | -| 40 | Final integration + documentation | All + Rui (tests) + Brent (QA) | Release candidate ready | +| 36 | Automation level refinement | Jeff + Luis | G1 automation enhancements | +| 37 | Cost estimation actors | Aditya | G5 estimation working | +| 38 | Error recovery mechanisms | Jeff | G2 checkpointing + rollback | +| 39 | Performance optimization | Luis + Jeff | Benchmarks passing | +| 40 | Final integration + documentation | All | Release candidate ready | ### Continuous Tasks (Throughout) **Brent (Quality - Independent, Low Contention)**: + - Review all PRs within 4 hours of submission - Run `nox -s typecheck` on all branches before merge - Run `nox -s lint` and ensure 0 warnings @@ -3194,6 +9459,7 @@ Deferred items remain planned but are not part of the 30-day MVP scope. - Security audit: no eval(), no template injection, no secrets in code **Rui (Testing - Parallel with Feature Work)**: + - Write Behave scenarios for each feature (before implementation starts) - Write Robot integration tests for each milestone - Run full test suite daily @@ -3203,10 +9469,10 @@ Deferred items remain planned but are not part of the 30-day MVP scope. ### Critical Path Dependencies ``` -Day 1: A5 (Persistence + Action Args) ───────────────────────────────┐ +Day 1: A5 (Persistence) ────────────────────────────────────────────┐ Day 2: B1.core/B2.persistence/B2.service/B3.cli (Project/Resource) ─┐│ Day 3: B4.sandbox (Sandbox) ────────────────────────────────────────┼┤ -Day 4: C1.schema/C2.legacy/C2.compiler (Actor) ─────────────────────┘│ +Day 4: C1.schema/C2.compiler (Actor) ───────────────────────────────┘│ Day 5: C3.protocol/C4.file (Skills) ─────────────────────────────────┤ Day 6: C4.search/C5.model (Change Tracking) ─────────────────────────┤ Day 7: C6.pipeline/C7.mcp/C8.providers (Validation + Providers) ─────┘ @@ -3227,11 +9493,11 @@ Day 22-30: CTX/G + F0 (Context + Large Project + Stubs) ─┘ | Risk | Mitigation | Owner | |------|------------|-------| -| Git worktree complexity | Use B4.sandbox git_worktree with pre-commit verification and isolation tests | Jeff | -| Multi-file generation reliability | Validate C9.execute/C9.apply flows with diff review artifacts and Robot E2E | Luis + Rui | -| Decision tree correction bugs | Jeff reviews D4 correction + checkpointing; add revert/append Behave coverage | Jeff | -| Large codebase performance | Profile CTX1/CTX2 indexing + hot/warm/cold tiers; enforce bounded memory tests | Luis + Hamza | -| Server connectivity stubs stability | Keep client-only stubs isolated; gate with feature flags and contract tests | Jeff + Luis | +| Git worktree complexity | Jeff handles sandbox implementation | Jeff | +| Multi-file generation reliability | Extensive testing, fallback mechanisms | Luis + Rui | +| Decision tree correction bugs | Jeff reviews all correction logic | Jeff | +| Large codebase performance | Early profiling, lazy loading | Luis + Hamza | +| Server mode stability | Incremental rollout, feature flags | Jeff + Luis | ### Definition of Done (Each Task) @@ -3251,6 +9517,7 @@ Day 22-30: CTX/G + F0 (Context + Large Project + Stubs) ─┘ ### M1: MVP (Day 7) - Minimally Usable for Source Code **End-to-end verification command sequence:** + ```bash # 1. Create an action cat > /tmp/test_action.yaml <=97%. +- [ ] Plan and Action records persist to SQLite database +- [ ] Phase transitions (ACTION → STRATEGIZE → EXECUTE → APPLY → APPLIED) work correctly +- [ ] Git worktree sandbox creates isolated working directory +- [ ] Changes in sandbox do not affect original until Apply +- [ ] At least 3 automation levels work (manual mode minimum) +- [ ] Error handling produces actionable messages +- [ ] Test coverage remains >85% ### M3: Full Plan Lifecycle with Actors (Day 14) **End-to-end verification:** + ```bash # Create actor YAML file cat > my_actor.yaml < # Should ``` **Technical Criteria:** -- Actor YAML files parse and validate correctly. -- Actors compile to LangGraph StateGraphs. -- Inline skill code executes in sandboxed environment. -- Built-in file skills (read/write/edit/delete) work. -- ChangeSet built from skill invocations (not parsed from output). -- Validation pipeline runs (syntax check, lint, tests). -- Multi-file generation produces correct ChangeSet. -- MCP skill adapter can connect to external servers (basic). +- [ ] Actor YAML files parse and validate correctly +- [ ] Actors compile to LangGraph StateGraphs +- [ ] Inline skill code executes in sandboxed environment +- [ ] Built-in file skills (read/write/edit/delete) work +- [ ] ChangeSet built from skill invocations (not parsed from output) +- [ ] Validation pipeline runs (syntax check, lint, tests) +- [ ] Multi-file generation produces correct ChangeSet +- [ ] MCP skill adapter can connect to external servers (basic) ### M4: Decision Tree & Correction (Day 21) **End-to-end verification:** + ```bash # Execute a plan to generate decisions agents [--data-dir PATH] [--config-path PATH] plan use local/complex-action local/large-project @@ -3382,21 +9651,22 @@ agents [--data-dir PATH] [--config-path PATH] plan tree ``` **Technical Criteria:** -- Decisions recorded during Strategize with full context snapshot. -- Decision tree persists to database. -- `agents [--data-dir PATH] [--config-path PATH] plan tree` displays ASCII tree correctly. -- `agents [--data-dir PATH] [--config-path PATH] plan explain` shows all decision details. -- Correction in revert mode: - - Archives old decisions. - - Rolls back sandbox to checkpoint. - - Re-executes from decision point. - - Generates new downstream decisions. -- Correction in append mode creates fix subplan. -- History preserved for comparison. +- [ ] Decisions recorded during Strategize with full context snapshot +- [ ] Decision tree persists to database +- [ ] `agents [--data-dir PATH] [--config-path PATH] plan tree` displays ASCII tree correctly +- [ ] `agents [--data-dir PATH] [--config-path PATH] plan explain` shows all decision details +- [ ] Correction in revert mode: + - [ ] Archives old decisions + - [ ] Rolls back sandbox to checkpoint + - [ ] Re-executes from decision point + - [ ] Generates new downstream decisions +- [ ] Correction in append mode creates fix subplan +- [ ] History preserved for comparison ### M5: Subplans & Parallel Execution (Day 25) **End-to-end verification:** + ```bash # Execute plan that spawns multiple subplans agents [--data-dir PATH] [--config-path PATH] plan use local/refactor-action local/monorepo @@ -3418,19 +9688,20 @@ agents [--data-dir PATH] [--config-path PATH] plan diff # Shows merge ``` **Technical Criteria:** -- SUBPLAN_SPAWN decisions created during Strategize. -- Subplans actually spawned during Execute. -- Sequential subplan execution works (one at a time). -- Parallel subplan execution works (with max_parallel limit). -- Each subplan has isolated sandbox. -- Three-way merge combines non-conflicting changes. -- Merge conflicts detected and marked. -- Parent plan tracks all subplan statuses. -- A plan with 10+ subplans completes successfully. +- [ ] SUBPLAN_SPAWN decisions created during Strategize +- [ ] Subplans actually spawned during Execute +- [ ] Sequential subplan execution works (one at a time) +- [ ] Parallel subplan execution works (with max_parallel limit) +- [ ] Each subplan has isolated sandbox +- [ ] Three-way merge combines non-conflicting changes +- [ ] Merge conflicts detected and marked +- [ ] Parent plan tracks all subplan statuses +- [ ] A plan with 10+ subplans completes successfully ### M6: Large Project Handling (Day 30) **End-to-end verification:** + ```bash # Index a large project (10,000+ files) agents [--data-dir PATH] [--config-path PATH] project create local/large-project @@ -3479,20 +9750,13 @@ agents [--data-dir PATH] [--config-path PATH] plan apply ``` **Technical Criteria:** -- Projects with 10,000+ files index without timeout. -- Context window management works (hot/warm/cold tiers). -- Hierarchical decomposition creates 4+ levels of subplans. -- Decision correction at any level recomputes only affected subtree. -- Parallel execution scales to 10+ concurrent subplans. -- Memory usage stays bounded (lazy context loading). -- A realistic porting task (500 file Python → TypeScript) completes autonomously. - -**M6 SUCCESS CRITERIA** (Day 30): -- 10,000+ file project indexes with bounded memory and hot/warm/cold tiering. -- Hierarchical decomposition reaches 4+ levels with correction limited to affected subtree. -- Parallel execution scales to 10+ subplans with merge and conflict handling. -- Autonomous porting task completes with validation and review gates. -- `nox` passes with coverage >=97% including large-project suites. +- [ ] Projects with 10,000+ files index without timeout +- [ ] Context window management works (hot/warm/cold tiers) +- [ ] Hierarchical decomposition creates 4+ levels of subplans +- [ ] Decision correction at any level recomputes only affected subtree +- [ ] Parallel execution scales to 10+ concurrent subplans +- [ ] Memory usage stays bounded (lazy context loading) +- [ ] A realistic porting task (500 file Python → TypeScript) completes autonomously --- @@ -3539,9 +9803,6 @@ DAY 4-7: ACTOR/SKILL LAYER │ [Aditya] C1.schema/C1.examples Actor Schema │ │ │ │ │ ▼ │ -│ [Jeff] C2.legacy Drop v2 actor configs │ -│ │ │ -│ ▼ │ │ [Aditya+Jeff] C2.loader/C2.compiler Actor Compiler │ │ │ │ │ [Jeff] C3.protocol/C3.context/C3.inline ═══► [Aditya] C7.mcp │ @@ -3618,12 +9879,18 @@ DAY 30: M6 TARGET ⊕ **Server connectivity (WORKSTREAM F) is deferred beyond the 30-day timeline. The server is a separate project—this implementation covers the client only.** The CleverAgents executable (`agents`) is purely a **client application** that can: + 1. Run in stand-alone local-only mode (no server required) 2. Connect to an independently developed CleverAgents server for multi-user/collaborative features -During Days 1-30, client stub infrastructure is delivered via Section 9 (F0.stubs), covering the connect command, client interfaces, local/remote detection, and NotImplementedError stubs. +During Days 1-30, the following client stub infrastructure should be created: +- [ ] Server client connection command (`agents [--data-dir PATH] [--config-path PATH] connect ` - stub) +- [ ] Abstract interfaces for client-to-server communication +- [ ] Resource abstraction that can detect local vs remote resources +- [ ] Placeholder client methods that return "Server connectivity not yet implemented" **What is NOT needed by Day 30:** + - Server implementation (the server is a separate project) - Full client-server API implementation - WebSocket client implementation diff --git a/notes.md b/notes.md new file mode 100644 index 000000000..d33f1a0b1 --- /dev/null +++ b/notes.md @@ -0,0 +1,12 @@ +# Some Notes For me + +This is a markdown file where I can jot down notes for myself. + + +## Error + +this error was reported: +Model: Opus 4.6 +``` +Error [193:63] Arguments missing for parameters "auto_build", "auto_apply", "confirm_apply", "max_context_size", "default_model" +``` -- 2.52.0 From a7d702e85d148b148232c5ea579dac1f1561ebcd Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Freeman Date: Wed, 11 Feb 2026 20:25:33 -0500 Subject: [PATCH 04/11] Docs: Updated implementation plan with new plan plus added asv requirements to commits --- implementation_plan.md | 5986 +++++++--------------------------------- 1 file changed, 962 insertions(+), 5024 deletions(-) diff --git a/implementation_plan.md b/implementation_plan.md index 422cba2b5..a8d1c17fb 100644 --- a/implementation_plan.md +++ b/implementation_plan.md @@ -1298,20 +1298,49 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [X] Add argument parsing for action parameters (`--arg name:type:required|optional:description`) - [X] Location: `src/cleveragents/domain/models/core/action.py` - [X] Tests: Behave scenarios for action model validation (22 scenarios in `features/action_model.feature`) - - [ ] **A2.1** [Luis] Extend Action model with additional fields (follow-up): - - [ ] Field `estimation_actor: str | None` - optional actor for cost/risk estimation - - [ ] Field `review_actor: str | None` - optional actor for code review - - [ ] Field `safety_profile: SafetyProfile | None` - safety constraints (DEFERRED to post-30; see Stage POST1) - - [ ] **A2.2** [Luis] Define `SafetyProfile` model (DEFERRED to post-30; see Stage POST1): - - [ ] Field `allowed_skill_categories: list[str] | None` - whitelist of skill types - - [ ] Field `require_checkpoints: bool` - require checkpointable skills - - [ ] Field `require_sandbox: bool` - require sandbox for all resources - - [ ] Field `require_human_approval: bool` - require approval at Apply - - [ ] Field `max_cost_usd: float | None` - budget cap - - [ ] Field `max_retries: int` - maximum retry attempts - - [ ] **A2.3** [Rui] Write tests for extended action model (DEFERRED to post-30; see Stage POST1): - - [ ] Scenario: Action with estimation_actor validates correctly - - [ ] Scenario: Safety profile enforced during execution + **Parallel Group A2b: Action/Plan Spec Alignment (M1-critical)** + **PARALLEL SUBTRACK A2b.alpha [Jeff]**: Action model alignment + invariants/automation metadata + **PARALLEL SUBTRACK A2b.beta [Luis]**: Plan metadata alignment + action linkage + **PARALLEL SUBTRACK A2b.gamma [Aditya]**: Action YAML schema + examples (config-first) + **SEQUENTIAL MERGE NOTE**: A2b.alpha + A2b.beta must land before A4b CLI wiring. + - [ ] **COMMIT (Owner: Jeff | Group: A2b.alpha) - Commit message: "feat(domain): align action metadata with invariants and automation profiles"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Update `src/cleveragents/domain/models/core/action.py` docstring to state actions are defined via YAML config and registered via CLI (remove "NOT YAML" wording). + - [ ] Code [Jeff]: Add `automation_profile` field (namespaced name string) to `Action` and validate `/` format + `local/` default handling. + - [ ] Code [Jeff]: Add `invariant_actor` (optional actor ref) and `invariants` list (action-scoped) with trimming, de-duplication, and empty-string rejection. + - [ ] Code [Jeff]: Add `definition_of_done_template` to preserve the pre-rendered DoD string before arg substitution; keep `definition_of_done` as rendered output. + - [ ] Code [Jeff]: Extend `ActionArgument` validation to enforce `min_value <= max_value`, regex only for string args, and default value type checks. + - [ ] Code [Jeff]: Add `Action.to_template_context()` (or equivalent) to generate deterministic arg context for templating. + - [ ] Docs [Jeff]: Update or create `docs/reference/action_model.md` with new fields, examples, and invariants/automation profile semantics. + - [ ] Tests (Behave) [Rui]: Add scenarios in `features/action_model.feature` for invariants validation, automation profile parsing, and definition_of_done_template retention. + - [ ] Tests (Robot) [Rui]: Add Robot scenario that loads action YAML and asserts invariants/automation profile fields are surfaced in CLI output (wired in A4b). + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_model_bench.py` to benchmark action argument parsing + template rendering. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(domain): align action metadata with invariants and automation profiles"`. + - [ ] **COMMIT (Owner: Luis | Group: A2b.beta) - Commit message: "feat(domain): align plan metadata with action linkage and automation profiles"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add `action_name` (namespaced name string) and `action_id` (ULID string) to `Plan` for traceability to the originating action. + - [ ] Code [Luis]: Add `automation_profile`, `invariant_actor`, and `invariants` fields to `Plan` with source tags (action/project/plan/global). + - [ ] Code [Luis]: Add `arguments` map (validated JSON-serializable values) and `definition_of_done_template` capture to the plan model for later re-rendering. + - [ ] Code [Luis]: Add execution metadata placeholders: `changeset_id`, `sandbox_refs`, `validation_summary`, and `decision_root_id` (optional until later stages). + - [ ] Code [Luis]: Enforce automation profile immutability after `plan use` and when phase progresses beyond Strategize. + - [ ] Docs [Luis]: Update `docs/reference/plan_model.md` to document new fields, immutability rules, and action linkage. + - [ ] Tests (Behave) [Rui]: Add scenarios in `features/plan_model.feature` for automation profile lock, invariant persistence, and action linkage fields. + - [ ] Tests (Robot) [Rui]: Add Robot scenario to inspect `plan status` output for action linkage and automation profile fields (once CLI aligned). + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_model_bench.py` for plan validation and serialization. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(domain): align plan metadata with action linkage and automation profiles"`. + - [ ] **COMMIT (Owner: Aditya | Group: A2b.gamma) - Commit message: "docs(action): add action YAML schema and examples"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Docs [Aditya]: Author `docs/schema/action.schema.yaml` with versioning, required fields, and explicit type constraints for actors, invariants, and arguments. + - [ ] Docs [Aditya]: Add example action configs under `examples/actions/` (simple, invariant-heavy, multi-project, and estimation-actor examples). + - [ ] Code [Aditya]: Add schema validation helper in `src/cleveragents/action/schema.py` that loads YAML, validates schema version, and returns typed data. + - [ ] Code [Aditya]: Add clear error messages for missing required fields and invalid namespaced names. + - [ ] Tests (Behave) [Rui]: Add scenarios that load each example YAML and assert schema validation passes; add invalid schema cases. + - [ ] Tests (Robot) [Rui]: Add a Robot smoke test that reads example YAML files and reports parse success/failure. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_schema_bench.py` for YAML schema validation throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Aditya]: `git commit -m "docs(action): add action YAML schema and examples"`. - [x] **Stage A3: Plan State Machine** (Day 1-2) - COMPLETED 2026-02-05 - [x] Code: Implement plan lifecycle state machine @@ -1339,545 +1368,191 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [X] `agents [--data-dir PATH] [--config-path PATH] plan cancel ` - cancel non-terminal plan - [X] Location: `src/cleveragents/cli/commands/action.py`, `src/cleveragents/cli/commands/plan.py` - [X] Tests: Behave tests for action CLI (15 scenarios in `features/action_cli.feature`) - - [ ] Tests: Behave tests for plan lifecycle CLI commands (pending) - - **[Rui]** Write 20 Behave scenarios in `features/plan_lifecycle_cli.feature` covering: - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan use` with valid action and project - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan use` with missing project error - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan use` with invalid action error - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan use` with argument validation - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan execute` on strategize-complete plan - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan execute` on non-strategize plan (error case) - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan apply` on execute-complete plan - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan apply` on non-execute plan (error case) - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan status` output format verification - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan list` filtering by phase - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan list` filtering by state - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan cancel` on active plan - - [ ] `agents [--data-dir PATH] [--config-path PATH] plan cancel` on already-applied plan (error case) - - [ ] Tests: Robot integration tests for CLI commands (pending) - - **[Rui]** Write Robot test suite `robot/plan_lifecycle_cli.robot` for end-to-end CLI testing + **Parallel Group A4b: Action/Plan CLI Spec Alignment + Tests (M1-critical)** + **PARALLEL SUBTRACK A4b.alpha [Jeff]**: CLI feature alignment + **PARALLEL SUBTRACK A4b.beta [Rui]**: Behave + Robot coverage + - [ ] **COMMIT (Owner: Jeff | Group: A4b.alpha) - Commit message: "feat(cli): support action create from YAML config"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add `--config/-c` to `agents action create`; load YAML via `action/schema.py` and fail fast on schema violations. + - [ ] Code [Jeff]: Implement override precedence (CLI flags override YAML fields; explicit CLI empty string clears YAML value). + - [ ] Code [Jeff]: Normalize namespaced names, actor refs, and argument definitions from YAML into `ActionArgument` objects. + - [ ] Code [Jeff]: Add `--update` guard (if action exists) or explicit error per spec; ensure idempotent update path is clear. + - [ ] Docs [Jeff]: Update CLI reference with YAML-based action creation + override examples and failure messages. + - [ ] Tests (Behave) [Rui]: Add scenarios in `features/action_cli.feature` covering valid config, overrides, invalid schema, and missing required fields. + - [ ] Tests (Robot) [Rui]: Add `robot/action_cli_from_config.robot` with end-to-end CLI flow and output assertions. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_cli_config_bench.py` for config parsing + normalization. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(cli): support action create from YAML config"`. + - [ ] **COMMIT (Owner: Jeff | Group: A4b.alpha) - Commit message: "feat(cli): extend plan use with invariants and automation profile"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add `--automation-profile`, `--invariant`, and `--invariant-actor` flags to `agents plan use` and plumb into PlanLifecycleService. + - [ ] Code [Jeff]: Resolve action name -> action_id, validate automation profile existence, and attach plan-scoped invariants. + - [ ] Code [Jeff]: Persist resolved invariants + profile in Plan metadata for later Strategize/Execute steps. + - [ ] Docs [Jeff]: Update `docs/reference/plan_cli.md` with examples for profile + invariants and error cases. + - [ ] Tests (Behave) [Rui]: Add scenarios in `features/plan_lifecycle_cli.feature` covering profile selection, invariant validation, and multiple projects. + - [ ] Tests (Robot) [Rui]: Add Robot tests for plan use with invariants and automation profiles (positive + negative cases). + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_use_cli_bench.py` for argument parsing and validation. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(cli): extend plan use with invariants and automation profile"`. + - [ ] **COMMIT (Owner: Rui | Group: A4b.beta) - Commit message: "test(cli): add plan lifecycle Behave and Robot coverage"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Tests (Behave) [Rui]: Add full coverage for `plan execute`, `plan apply`, `plan status`, `plan list`, `plan cancel` (success + error paths). + - [ ] Tests (Robot) [Rui]: Add `robot/plan_lifecycle_cli.robot` covering end-to-end lifecycle transitions. + - [ ] Docs [Rui]: Update `docs/development/testing.md` with new CLI suites and command mappings. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_cli_smoke_bench.py` for CLI argument parsing overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "test(cli): add plan lifecycle Behave and Robot coverage"`. -- [ ] **Stage A5: Plan Persistence** (Day 1-2) **[Jeff + Luis - Critical Path]** - - **PARALLEL SUBTRACK A5.alpha [Jeff - Day 1 AM]**: Database Schema (A5.1, A5.2) - **PARALLEL SUBTRACK A5.beta [Luis - Day 1 AM]**: SQLAlchemy Models (A5.3, A5.4) - can start with schema design doc - **SEQUENTIAL AFTER alpha+beta [Jeff - Day 1 PM]**: Repository Implementation (A5.5, A5.6) - **SEQUENTIAL AFTER repos [Jeff - Day 2 AM]**: Service Integration (A5.7, A5.8) - **PARALLEL CONTINUOUS [Rui - Day 1-2]**: Test Writing (A5.9, A5.10, A5.11) - - - [ ] Code: Plan database schema and repository - - [ ] **A5.1** [Jeff] Create Alembic migration for `lifecycle_plans` table in `alembic/versions/xxx_add_lifecycle_plans.py`: - - [ ] **A5.1a** [Jeff] Create migration file with `revision` and `down_revision` links: - - [ ] Run `alembic revision -m "add_lifecycle_plans_table"` to generate file - - [ ] Verify revision ID is unique - - [ ] Set `down_revision` to point to previous migration (likely actions table) - - [ ] Commit: "feat(db): add lifecycle_plans migration scaffold" - - [ ] **A5.1b** [Jeff] Define `lifecycle_plans` table schema in upgrade() function: - - [ ] Column `plan_id` TEXT PRIMARY KEY (ULID format, validated at application layer) - - [ ] Column `parent_plan_id` TEXT NULLABLE FK references lifecycle_plans(plan_id) - for subplan hierarchy - - [ ] Column `root_plan_id` TEXT NULLABLE FK references lifecycle_plans(plan_id) - always points to topmost plan - - [ ] Column `action_id` TEXT NOT NULL FK references actions(action_id) - the action template used - - [ ] Column `phase` TEXT NOT NULL CHECK(phase IN ('ACTION','STRATEGIZE','EXECUTE','APPLY','APPLIED')) - lifecycle phase - - [ ] Column `state` TEXT NOT NULL - processing state within phase (available/draft/archived for ACTION; queued/processing/errored/complete/cancelled for others) - - [ ] Column `attempt` INTEGER NOT NULL DEFAULT 1 - increments on re-execution after correction - - [ ] Column `automation_level` TEXT NOT NULL DEFAULT 'manual' CHECK(automation_level IN ('manual','review_before_apply','full_automation')) - - [ ] Column `project_ids` TEXT NOT NULL - JSON array of project ULIDs this plan operates on - - [ ] Column `arguments` TEXT NULLABLE - JSON object mapping argument name to provided value - - [ ] Column `strategy_context` TEXT NULLABLE - JSON blob storing Strategize phase outputs (strategy, execution blueprint, resource queries) - - [ ] Column `execution_log` TEXT NULLABLE - JSON array of execution events [{timestamp, event_type, details}] - - [ ] Column `changeset_id` TEXT NULLABLE FK references changesets(changeset_id) - link to generated changes - - [ ] Column `sandbox_refs` TEXT NULLABLE - JSON object mapping resource_id to sandbox_path - - [ ] Column `error_message` TEXT NULLABLE - last error message if state is errored - - [ ] Column `created_at` TEXT NOT NULL - ISO8601 timestamp of plan creation - - [ ] Column `updated_at` TEXT NOT NULL - ISO8601 timestamp of last modification - - [ ] Column `completed_at` TEXT NULLABLE - ISO8601 timestamp when plan reached terminal state - - [ ] Column `created_by` TEXT NULLABLE - user/session identifier who created the plan - - [ ] Commit: "feat(db): define lifecycle_plans table columns" - - [ ] **A5.1c** [Jeff] Create indices for common queries: - - [ ] Index `ix_lifecycle_plans_phase` on `phase` - for phase-based filtering - - [ ] Index `ix_lifecycle_plans_state` on `state` - for state-based filtering - - [ ] Index `ix_lifecycle_plans_parent` on `parent_plan_id` - for subplan lookups - - [ ] Index `ix_lifecycle_plans_root` on `root_plan_id` - for full tree queries - - [ ] Index `ix_lifecycle_plans_created` on `created_at` - for recent plans - - [ ] Index `ix_lifecycle_plans_action` on `action_id` - for action usage lookups - - [ ] Index `ix_lifecycle_plans_project` on `project_ids` - for project-based queries (use json_extract if needed) - - [ ] Commit: "feat(db): add lifecycle_plans indices" - - [ ] **A5.1d** [Jeff] Define foreign key ON DELETE behaviors: - - [ ] parent_plan_id: ON DELETE SET NULL - orphan subplans if parent deleted (preserve for debugging) - - [ ] root_plan_id: ON DELETE SET NULL - same reasoning - - [ ] action_id: ON DELETE RESTRICT - cannot delete action if plans exist using it - - [ ] changeset_id: ON DELETE SET NULL - preserve plan record even if changeset cleaned up - - [ ] Commit: "feat(db): define lifecycle_plans FK constraints" - - [ ] **A5.1e** [Jeff] Write `downgrade()` function to drop table: - - [ ] Drop all indices first - - [ ] Drop the lifecycle_plans table - - [ ] Verify downgrade works with `alembic downgrade -1` - - [ ] Commit: "feat(db): add lifecycle_plans downgrade function" - - [ ] **A5.2** [Jeff] Create Alembic migration for `actions` table in `alembic/versions/xxx_add_actions.py`: - - [ ] **A5.2a** [Jeff] Create migration file: - - [ ] Run `alembic revision -m "add_actions_table"` - - [ ] This migration MUST run BEFORE lifecycle_plans (set down_revision appropriately) - - [ ] Commit: "feat(db): add actions migration scaffold" - - [ ] **A5.2b** [Jeff] Define `actions` table schema: - - [ ] Column `action_id` TEXT PRIMARY KEY - ULID format - - [ ] Column `name` TEXT NOT NULL - full namespaced name (e.g., "local/code-coverage", "myorg/deploy-action") - - [ ] Column `namespace` TEXT NOT NULL - extracted namespace portion for filtering (e.g., "local", "myorg") - - [ ] Column `short_name` TEXT NOT NULL - extracted name portion after namespace (e.g., "code-coverage") - - [ ] Column `description` TEXT NULLABLE - human-readable description - - [ ] Column `definition_of_done` TEXT NOT NULL - explicit testable completion criteria (must/should/may format) - - [ ] Column `strategy_actor` TEXT NOT NULL - namespaced actor reference for Strategize phase (e.g., "local/coverage-strategist") - - [ ] Column `execution_actor` TEXT NOT NULL - namespaced actor reference for Execute phase - - [ ] Column `estimation_actor` TEXT NULLABLE - optional actor for cost/risk estimation (runs after Strategize) - - [ ] Column `review_actor` TEXT NULLABLE - optional actor for code review - - [ ] Column `inputs_schema` TEXT NOT NULL DEFAULT '[]' - JSON array of ActionArgument definitions - - [ ] Column `state` TEXT NOT NULL DEFAULT 'draft' CHECK(state IN ('available','draft','archived')) - - [ ] Column `reusable` BOOLEAN NOT NULL DEFAULT TRUE - if false, action self-deletes after first use - - [ ] Column `read_only` BOOLEAN NOT NULL DEFAULT FALSE - if true, only read-only skills allowed - - [ ] Column `safety_profile` TEXT NULLABLE - JSON object for SafetyProfile constraints (DEFERRED to post-30; see POST1) - - [ ] Column `created_at` TEXT NOT NULL - ISO8601 creation timestamp - - [ ] Column `updated_at` TEXT NOT NULL - ISO8601 last modification timestamp - - [ ] Commit: "feat(db): define actions table columns" - - [ ] **A5.2c** [Jeff] Create indices: - - [ ] UNIQUE index on `name` - enforce unique namespaced names - - [ ] Index `ix_actions_namespace` on `namespace` - for namespace filtering - - [ ] Index `ix_actions_state` on `state` - for state filtering - - [ ] Index `ix_actions_short_name` on `short_name` - for partial name searches - - [ ] Commit: "feat(db): add actions indices" - - [ ] **A5.2d** [Jeff] Write `downgrade()` function: - - [ ] Drop indices and table - - [ ] Verify with `alembic downgrade -1` - - [ ] Commit: "feat(db): add actions downgrade function" - - [ ] **A5.3** [Luis] Create `LifecyclePlanModel` SQLAlchemy model in `src/cleveragents/infrastructure/database/models.py`: - - [ ] **A5.3a** [Luis] Define class structure: - - [ ] Create class `LifecyclePlanModel(Base)` with `__tablename__ = 'lifecycle_plans'` - - [ ] Import necessary SQLAlchemy types: `Column, String, Integer, Boolean, Text, ForeignKey, DateTime` - - [ ] Import relationship types: `relationship, backref` - - [ ] Commit: "feat(models): add LifecyclePlanModel class scaffold" - - [ ] **A5.3b** [Luis] Define all columns matching migration schema: - - [ ] `plan_id = Column(String(26), primary_key=True)` - ULID is 26 chars - - [ ] `parent_plan_id = Column(String(26), ForeignKey('lifecycle_plans.plan_id', ondelete='SET NULL'), nullable=True)` - - [ ] `root_plan_id = Column(String(26), ForeignKey('lifecycle_plans.plan_id', ondelete='SET NULL'), nullable=True)` - - [ ] `action_id = Column(String(26), ForeignKey('actions.action_id', ondelete='RESTRICT'), nullable=False)` - - [ ] `phase = Column(String(20), nullable=False)` - enum handled at domain layer - - [ ] `state = Column(String(20), nullable=False)` - - [ ] `attempt = Column(Integer, nullable=False, default=1)` - - [ ] `automation_level = Column(String(30), nullable=False, default='manual')` - - [ ] `project_ids = Column(Text, nullable=False)` - JSON string - - [ ] `arguments = Column(Text, nullable=True)` - JSON string - - [ ] `strategy_context = Column(Text, nullable=True)` - large JSON blob - - [ ] `execution_log = Column(Text, nullable=True)` - JSON array - - [ ] `changeset_id = Column(String(26), nullable=True)` - - [ ] `sandbox_refs = Column(Text, nullable=True)` - JSON object - - [ ] `error_message = Column(Text, nullable=True)` - - [ ] `created_at = Column(String(30), nullable=False)` - ISO8601 - - [ ] `updated_at = Column(String(30), nullable=False)` - - [ ] `completed_at = Column(String(30), nullable=True)` - - [ ] `created_by = Column(String(255), nullable=True)` - - [ ] Commit: "feat(models): define LifecyclePlanModel columns" - - [ ] **A5.3c** [Luis] Define relationships: - - [ ] `parent_plan = relationship('LifecyclePlanModel', remote_side=[plan_id], backref='children', foreign_keys=[parent_plan_id])` - - [ ] `action = relationship('ActionModel', backref='plans')` - - [ ] NOTE: root_plan relationship not needed as query pattern is different - - [ ] Commit: "feat(models): define LifecyclePlanModel relationships" - - [ ] **A5.3d** [Luis] Implement `to_domain() -> Plan` method: - - [ ] Import `Plan, PlanPhase, ProcessingState, AutomationLevel` from domain - - [ ] Convert `phase` string to `PlanPhase` enum: `PlanPhase[self.phase]` - - [ ] Convert `state` string to appropriate state enum based on phase - - [ ] Parse `project_ids` JSON: `json.loads(self.project_ids)` with error handling - - [ ] Parse `arguments` JSON if not None: `json.loads(self.arguments) if self.arguments else None` - - [ ] Parse `strategy_context` JSON if not None - - [ ] Parse `execution_log` JSON if not None - - [ ] Parse `sandbox_refs` JSON if not None - - [ ] Convert timestamp strings to `datetime.fromisoformat()` objects - - [ ] Construct and return `Plan(plan_id=self.plan_id, ...)` - - [ ] Add comprehensive docstring explaining the conversion - - [ ] Commit: "feat(models): implement LifecyclePlanModel.to_domain()" - - [ ] **A5.3e** [Luis] Implement classmethod `from_domain(plan: Plan) -> LifecyclePlanModel`: - - [ ] Add `@classmethod` decorator - - [ ] Convert `plan.phase.name` to string for phase column - - [ ] Convert state enum `.name` to string - - [ ] Serialize `project_ids` to JSON: `json.dumps(plan.project_ids)` - - [ ] Serialize `arguments` to JSON if not None - - [ ] Serialize `strategy_context` to JSON if not None (handle nested objects) - - [ ] Serialize `execution_log` to JSON if not None - - [ ] Serialize `sandbox_refs` to JSON if not None - - [ ] Convert datetime objects to `.isoformat()` strings - - [ ] Return constructed `LifecyclePlanModel` instance - - [ ] Commit: "feat(models): implement LifecyclePlanModel.from_domain()" - - [ ] **A5.4** [Luis] Create `ActionModel` SQLAlchemy model in `src/cleveragents/infrastructure/database/models.py`: - - [ ] **A5.4a** [Luis] Define class structure and columns: - - [ ] Create class `ActionModel(Base)` with `__tablename__ = 'actions'` - - [ ] `action_id = Column(String(26), primary_key=True)` - - [ ] `name = Column(String(255), nullable=False, unique=True)` - - [ ] `namespace = Column(String(100), nullable=False)` - - [ ] `short_name = Column(String(150), nullable=False)` - - [ ] `description = Column(Text, nullable=True)` - - [ ] `definition_of_done = Column(Text, nullable=False)` - - [ ] `strategy_actor = Column(String(255), nullable=False)` - - [ ] `execution_actor = Column(String(255), nullable=False)` - - [ ] `estimation_actor = Column(String(255), nullable=True)` - - [ ] `review_actor = Column(String(255), nullable=True)` - - [ ] `inputs_schema = Column(Text, nullable=False, default='[]')` - - [ ] `state = Column(String(20), nullable=False, default='draft')` - - [ ] `reusable = Column(Boolean, nullable=False, default=True)` - - [ ] `read_only = Column(Boolean, nullable=False, default=False)` - - [ ] `safety_profile = Column(Text, nullable=True)` (DEFERRED to post-30; see POST1) - - [ ] `created_at = Column(String(30), nullable=False)` - - [ ] `updated_at = Column(String(30), nullable=False)` - - [ ] Commit: "feat(models): define ActionModel columns" - - [ ] **A5.4b** [Luis] Implement `to_domain() -> Action` method: - - [ ] Import `Action, ActionState, ActionArgument` from domain - - [ ] Convert `state` string to `ActionState` enum - - [ ] Parse `inputs_schema` JSON and convert to `list[ActionArgument]` - - [ ] Parse `safety_profile` JSON if present to `SafetyProfile` or None (DEFERRED to post-30; see POST1) - - [ ] Convert timestamps to datetime objects - - [ ] Construct and return `Action` instance - - [ ] Commit: "feat(models): implement ActionModel.to_domain()" - - [ ] **A5.4c** [Luis] Implement classmethod `from_domain(action: Action) -> ActionModel`: - - [ ] Extract namespace and short_name from action.name using `NamespacedName.parse()` - - [ ] Serialize `inputs_schema` to JSON from list of ActionArgument (call `.model_dump()` on each) - - [ ] Serialize `safety_profile` to JSON if present (DEFERRED to post-30; see POST1) - - [ ] Convert timestamps to ISO8601 strings - - [ ] Return constructed `ActionModel` instance - - [ ] Commit: "feat(models): implement ActionModel.from_domain()" - - [ ] **A5.5** [Jeff] Implement `LifecyclePlanRepository` in `src/cleveragents/infrastructure/database/repositories.py`: - - [ ] **A5.5a** [Jeff] Define class structure: - - [ ] Create class `LifecyclePlanRepository` with proper typing - - [ ] Add `__init__(self, session_factory: Callable[[], Session])` - session factory injection - - [ ] Store `self._session_factory = session_factory` - - [ ] Add class docstring explaining repository pattern usage - - [ ] Commit: "feat(repo): add LifecyclePlanRepository scaffold" - - [ ] **A5.5b** [Jeff] Implement `create(plan: Plan) -> Plan`: - - [ ] Open session using context manager: `with self._session_factory() as session:` - - [ ] Convert domain model: `model = LifecyclePlanModel.from_domain(plan)` - - [ ] Add to session: `session.add(model)` - - [ ] Commit transaction: `session.commit()` - - [ ] Refresh to get any database-generated values: `session.refresh(model)` - - [ ] Convert back and return: `return model.to_domain()` - - [ ] Wrap in try/except for `IntegrityError` - raise custom `DuplicatePlanError` if duplicate ID - - [ ] Add type hints and docstring - - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.create()" - - [ ] **A5.5c** [Jeff] Implement `get_by_id(plan_id: str) -> Plan | None`: - - [ ] Query by primary key: `session.query(LifecyclePlanModel).filter_by(plan_id=plan_id).first()` - - [ ] Return `None` if not found - - [ ] Convert to domain model if found - - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.get_by_id()" - - [ ] **A5.5d** [Jeff] Implement `get_by_phase(phase: PlanPhase, limit: int = 100) -> list[Plan]`: - - [ ] Filter by phase column: `.filter_by(phase=phase.name)` - - [ ] Order by created_at DESC: `.order_by(LifecyclePlanModel.created_at.desc())` - - [ ] Apply limit: `.limit(limit)` - - [ ] Convert all results to domain models using list comprehension - - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.get_by_phase()" - - [ ] **A5.5e** [Jeff] Implement `get_by_state(state: ProcessingState, limit: int = 100) -> list[Plan]`: - - [ ] Similar pattern to get_by_phase - - [ ] Filter by state column - - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.get_by_state()" - - [ ] **A5.5f** [Jeff] Implement `get_children(parent_plan_id: str) -> list[Plan]`: - - [ ] Filter by parent_plan_id: `.filter_by(parent_plan_id=parent_plan_id)` - - [ ] Order by created_at ASC (oldest first for processing order) - - [ ] Convert all to domain models - - [ ] Used for listing direct subplans - - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.get_children()" - - [ ] **A5.5g** [Jeff] Implement `get_tree(root_plan_id: str) -> list[Plan]`: - - [ ] Use recursive CTE query for all descendants: - ```python - from sqlalchemy import text - cte = text(''' - WITH RECURSIVE plan_tree AS ( - SELECT * FROM lifecycle_plans WHERE plan_id = :root_id - UNION ALL - SELECT lp.* FROM lifecycle_plans lp - INNER JOIN plan_tree pt ON lp.parent_plan_id = pt.plan_id - ) - SELECT * FROM plan_tree ORDER BY created_at ASC - ''') - ``` - - [ ] Execute and map results to domain models - - [ ] Return in tree order (parent before children by creation time) - - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.get_tree()" - - [ ] **A5.5h** [Jeff] Implement `update(plan: Plan) -> Plan`: - - [ ] Fetch existing record by plan_id - - [ ] Raise `PlanNotFoundError` if not exists - - [ ] Update all fields from domain model (use a helper to copy attributes) - - [ ] Auto-update `updated_at` timestamp to now - - [ ] Commit transaction - - [ ] Return updated plan (re-query to ensure consistency) - - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.update()" - - [ ] **A5.5i** [Jeff] Implement `list_all(limit: int = 100, offset: int = 0) -> list[Plan]`: - - [ ] Query all with pagination: `.offset(offset).limit(limit)` - - [ ] Order by created_at DESC - - [ ] Convert to domain models - - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.list_all()" - - [ ] **A5.5j** [Jeff] Implement `count(phase: PlanPhase | None = None, state: ProcessingState | None = None) -> int`: - - [ ] Use `session.query(func.count(LifecyclePlanModel.plan_id))` - - [ ] Apply optional phase filter - - [ ] Apply optional state filter - - [ ] Return `.scalar()` result - - [ ] Commit: "feat(repo): implement LifecyclePlanRepository.count()" - - [ ] **A5.5k** [Jeff] Add `@retry_database` decorator to all methods: - - [ ] Import from `src/cleveragents/core/retry_patterns.py` - - [ ] Configure: 3 retries, exponential backoff (1s, 2s, 4s) - - [ ] Only retry on `OperationalError` (database locked, connection timeout) - - [ ] Do NOT retry on `IntegrityError` (these are application logic errors) - - [ ] Commit: "feat(repo): add retry decorator to LifecyclePlanRepository" - - [ ] **A5.6** [Luis] Implement `ActionRepository` in `src/cleveragents/infrastructure/database/repositories.py`: - - [ ] **A5.6a** [Luis] Define class with session factory injection: - - [ ] Create class `ActionRepository` - - [ ] Add `__init__(self, session_factory: Callable[[], Session])` - - [ ] Commit: "feat(repo): add ActionRepository scaffold" - - [ ] **A5.6b** [Luis] Implement `create(action: Action) -> Action`: - - [ ] Same pattern as LifecyclePlanRepository - - [ ] Handle duplicate name error specifically - - [ ] Commit: "feat(repo): implement ActionRepository.create()" - - [ ] **A5.6c** [Luis] Implement `get_by_id(action_id: str) -> Action | None`: - - [ ] Query by primary key - - [ ] Convert to domain or return None - - [ ] Commit: "feat(repo): implement ActionRepository.get_by_id()" - - [ ] **A5.6d** [Luis] Implement `get_by_name(name: str) -> Action | None`: - - [ ] Query by exact namespaced name match: `.filter_by(name=name).first()` - - [ ] Used for `agents [--data-dir PATH] [--config-path PATH] action show local/my-action` - - [ ] Commit: "feat(repo): implement ActionRepository.get_by_name()" - - [ ] **A5.6e** [Luis] Implement `get_by_namespace(namespace: str, state: ActionState | None = None) -> list[Action]`: - - [ ] Filter by namespace column - - [ ] Optionally filter by state - - [ ] Order by short_name ASC for consistent display - - [ ] Convert all to domain models - - [ ] Commit: "feat(repo): implement ActionRepository.get_by_namespace()" - - [ ] **A5.6f** [Luis] Implement `get_by_state(state: ActionState) -> list[Action]`: - - [ ] Filter by state column - - [ ] Order by updated_at DESC (most recently modified first) - - [ ] Commit: "feat(repo): implement ActionRepository.get_by_state()" - - [ ] **A5.6g** [Luis] Implement `update(action: Action) -> Action`: - - [ ] Fetch by action_id - - [ ] Update all fields - - [ ] Auto-update updated_at - - [ ] Commit and return - - [ ] Commit: "feat(repo): implement ActionRepository.update()" - - [ ] **A5.6h** [Luis] Implement `list_available(namespace: str | None = None) -> list[Action]`: - - [ ] Filter by state='available' - - [ ] Optionally filter by namespace - - [ ] Order by namespace ASC, short_name ASC - - [ ] Used for `agents [--data-dir PATH] [--config-path PATH] action list` - - [ ] Commit: "feat(repo): implement ActionRepository.list_available()" - - [ ] **A5.6i** [Luis] Implement `delete(action_id: str) -> bool`: - - [ ] First check if any plans reference this action: `plan_repo.count(action_id=action_id)` - - [ ] If plans exist, raise `ActionInUseError` with count of plans - - [ ] Otherwise delete the action - - [ ] Return True if deleted - - [ ] Commit: "feat(repo): implement ActionRepository.delete()" - - [ ] **A5.6j** [Luis] Add retry decorator to all methods: - - [ ] Same pattern as LifecyclePlanRepository - - [ ] Commit: "feat(repo): add retry decorator to ActionRepository" - - [ ] **A5.7** [Jeff] Update `PlanLifecycleService` to use repositories: - - [ ] **A5.7a** [Jeff] Modify `__init__()` to accept repository dependencies: - - [ ] Change signature: `def __init__(self, plan_repository: LifecyclePlanRepository, action_repository: ActionRepository):` - - [ ] Store as instance variables: `self._plan_repo = plan_repository`, `self._action_repo = action_repository` - - [ ] REMOVE the in-memory storage: Delete `self._plans: dict` and `self._actions: dict` - - [ ] Update docstring to reflect dependency injection - - [ ] Commit: "refactor(service): update PlanLifecycleService to inject repositories" - - [ ] **A5.7b** [Jeff] Update `create_action()` to use ActionRepository: - - [ ] Replace `self._actions[action.action_id] = action` with `self._action_repo.create(action)` - - [ ] Handle `DuplicateActionError` by converting to user-friendly error message - - [ ] Return the created action from repository (may have database-modified fields) - - [ ] Commit: "refactor(service): update create_action() to use repository" - - [ ] **A5.7c** [Jeff] Update `get_action()` to use repository: - - [ ] Replace dict lookup with `self._action_repo.get_by_id()` or `get_by_name()` - - [ ] Handle both ID and namespaced name lookups - - [ ] Commit: "refactor(service): update get_action() to use repository" - - [ ] **A5.7d** [Jeff] Update `list_actions()` to use repository: - - [ ] Replace dict.values() iteration with `self._action_repo.list_available()` - - [ ] Add namespace filter parameter - - [ ] Add state filter parameter - - [ ] Commit: "refactor(service): update list_actions() to use repository" - - [ ] **A5.7e** [Jeff] Update `use_action()` to use both repositories: - - [ ] Fetch action from ActionRepository by name - - [ ] Raise `ActionNotFoundError` if not exists - - [ ] Raise `ActionNotAvailableError` if action.state != AVAILABLE - - [ ] Create new Plan domain object with ULID, set action_id reference - - [ ] Persist plan via LifecyclePlanRepository.create() - - [ ] Return the created plan - - [ ] Commit: "refactor(service): update use_action() to use repositories" - - [ ] **A5.7f** [Jeff] Update all plan state transition methods: - - [ ] `start_strategize()`: fetch plan → verify phase → update state → save - - [ ] `complete_strategize()`: fetch → verify → update phase+state → save - - [ ] `fail_strategize()`: fetch → update state to ERRORED → set error_message → save - - [ ] `start_execute()`: same pattern - - [ ] `complete_execute()`: same pattern, store changeset_id - - [ ] `fail_execute()`: same pattern - - [ ] `apply_plan()`: verify Execute phase complete → update to APPLIED → set completed_at → save - - [ ] All methods must re-fetch after save to return current state - - [ ] Commit: "refactor(service): update phase transition methods to use repository" - - [ ] **A5.7g** [Jeff] Update `cancel_plan()` to use repository: - - [ ] Fetch plan - - [ ] Verify not in terminal state (APPLIED or CANCELLED) - - [ ] Set state to CANCELLED, set completed_at - - [ ] Persist via repository - - [ ] Commit: "refactor(service): update cancel_plan() to use repository" - - [ ] **A5.7h** [Jeff] Add transaction handling for multi-step operations: - - [ ] For operations that modify multiple entities (e.g., use_action creates plan + may update action): - - [ ] Use UnitOfWork pattern: start transaction, do all operations, commit atomically - - [ ] If any step fails, rollback all changes - - [ ] Create `UnitOfWork` class if not exists: manages session lifecycle - - [ ] Commit: "feat(service): add transaction handling for multi-step operations" - - [ ] **A5.8** [Luis] Update DI container in `src/cleveragents/application/container.py`: - - [ ] **A5.8a** [Luis] Add `LifecyclePlanRepository` provider: - - [ ] Create factory function that instantiates repository with session factory - - [ ] Register with container - - [ ] Ensure proper scoping (singleton or per-request based on usage pattern) - - [ ] Commit: "feat(di): add LifecyclePlanRepository provider" - - [ ] **A5.8b** [Luis] Add `ActionRepository` provider: - - [ ] Same pattern as plan repository - - [ ] Commit: "feat(di): add ActionRepository provider" - - [ ] **A5.8c** [Luis] Update `PlanLifecycleService` provider to inject repositories: - - [ ] Modify service factory to resolve both repositories - - [ ] Pass to PlanLifecycleService constructor - - [ ] Verify dependency chain is correct - - [ ] Commit: "feat(di): update PlanLifecycleService provider with repositories" - - [ ] Tests: Integration tests for plan/action persistence - - [ ] **A5.9** [Rui] Write Behave scenarios in `features/plan_persistence.feature`: - - [ ] **A5.9a** [Rui] Scenario: Create plan stores record in database - - [ ] Given: An action "local/test-action" exists in database with state=AVAILABLE - - [ ] And: A project "local/test-project" exists - - [ ] When: I call `plan_service.use_action("local/test-action", project_ids=["proj-123"])` - - [ ] Then: A plan record exists in the lifecycle_plans table - - [ ] And: The plan_id is a valid 26-character ULID - - [ ] And: The plan.phase is STRATEGIZE - - [ ] And: The plan.state is QUEUED - - [ ] And: The plan.action_id matches the action - - [ ] Commit: "test(behave): add plan creation persistence scenario" - - [ ] **A5.9b** [Rui] Scenario: Update plan phase persists correctly - - [ ] Given: A plan exists in database with phase=STRATEGIZE, state=QUEUED - - [ ] When: I call `plan_service.complete_strategize(plan_id, strategy_context={...})` - - [ ] And: I call `plan_service.start_execute(plan_id)` - - [ ] Then: The database record shows phase='EXECUTE' - - [ ] And: The database record shows state='PROCESSING' - - [ ] And: The updated_at timestamp has changed - - [ ] Commit: "test(behave): add plan phase update persistence scenario" - - [ ] **A5.9c** [Rui] Scenario: Query plans by phase returns filtered results - - [ ] Given: 3 plans exist: 1 in STRATEGIZE, 1 in EXECUTE, 1 in APPLIED - - [ ] When: I query `plan_repo.get_by_phase(PlanPhase.STRATEGIZE)` - - [ ] Then: Only 1 plan is returned - - [ ] And: Its phase is STRATEGIZE - - [ ] Commit: "test(behave): add plan phase query scenario" - - [ ] **A5.9d** [Rui] Scenario: Query plans by state returns filtered results - - [ ] Given: 3 plans exist: 1 QUEUED, 1 PROCESSING, 1 ERRORED - - [ ] When: I query `plan_repo.get_by_state(ProcessingState.ERRORED)` - - [ ] Then: Only 1 plan is returned - - [ ] And: Its state is ERRORED - - [ ] Commit: "test(behave): add plan state query scenario" - - [ ] **A5.9e** [Rui] Scenario: Get plan tree returns parent and all children - - [ ] Given: A root plan exists with plan_id="root-123" - - [ ] And: A child plan exists with parent_plan_id="root-123" - - [ ] And: A grandchild plan exists with parent_plan_id=child_plan_id - - [ ] When: I query `plan_repo.get_tree("root-123")` - - [ ] Then: 3 plans are returned in order - - [ ] And: First plan is the root - - [ ] And: Second plan is the child - - [ ] And: Third plan is the grandchild - - [ ] Commit: "test(behave): add plan tree query scenario" - - [ ] **A5.9f** [Rui] Scenario: Concurrent plan creation is thread-safe - - [ ] Given: An action exists - - [ ] When: 10 threads simultaneously call `plan_service.use_action()` - - [ ] Then: All 10 plans are created successfully - - [ ] And: All 10 plan_ids are unique - - [ ] And: No database integrity errors occurred - - [ ] Commit: "test(behave): add concurrent plan creation scenario" - - [ ] **A5.10** [Rui] Write Behave scenarios in `features/action_persistence.feature`: - - [ ] **A5.10a** [Rui] Scenario: Create action stores record in database - - [ ] Given: No action named "local/test-action" exists - - [ ] When: I call `action_service.create_action()` with valid parameters - - [ ] Then: An action record exists in the actions table - - [ ] And: The action_id is a valid 26-character ULID - - [ ] And: The namespace column is "local" - - [ ] And: The short_name column is "test-action" - - [ ] Commit: "test(behave): add action creation persistence scenario" - - [ ] **A5.10b** [Rui] Scenario: Get action by namespaced name works - - [ ] Given: An action "local/my-action" exists in database - - [ ] When: I call `action_repo.get_by_name("local/my-action")` - - [ ] Then: The action is returned - - [ ] And: Its name matches "local/my-action" - - [ ] Commit: "test(behave): add action name lookup scenario" - - [ ] **A5.10c** [Rui] Scenario: List available excludes archived actions - - [ ] Given: 3 actions exist: 2 with state=AVAILABLE, 1 with state=ARCHIVED - - [ ] When: I call `action_repo.list_available()` - - [ ] Then: Only 2 actions are returned - - [ ] And: Neither has state=ARCHIVED - - [ ] Commit: "test(behave): add action list available scenario" - - [ ] **A5.10d** [Rui] Scenario: Update action state persists - - [ ] Given: An action exists with state=DRAFT - - [ ] When: I call `action_service.make_available(action_id)` - - [ ] Then: The database record shows state='AVAILABLE' - - [ ] Commit: "test(behave): add action state update scenario" - - [ ] **A5.10e** [Rui] Scenario: Delete action with existing plans fails - - [ ] Given: An action "local/used-action" exists - - [ ] And: A plan exists that references this action - - [ ] When: I call `action_repo.delete(action_id)` - - [ ] Then: An ActionInUseError is raised - - [ ] And: The action still exists in the database - - [ ] Commit: "test(behave): add action delete protection scenario" - - [ ] **A5.11** [Rui] Write Robot test `robot/plan_persistence_e2e.robot`: - - [ ] **A5.11a** [Rui] Test: Full lifecycle persists all transitions - - [ ] Create action via CLI: `agents [--data-dir PATH] [--config-path PATH] action create --name local/e2e-test ...` - - [ ] Make action available: `agents [--data-dir PATH] [--config-path PATH] action available ` - - [ ] Create project: `agents [--data-dir PATH] [--config-path PATH] project create --name local/e2e-project` - - [ ] Use action on project: `agents [--data-dir PATH] [--config-path PATH] plan use local/e2e-test --project local/e2e-project` - - [ ] Execute plan: `agents [--data-dir PATH] [--config-path PATH] plan execute ` - - [ ] Apply plan: `agents [--data-dir PATH] [--config-path PATH] plan apply ` - - [ ] Verify via `agents [--data-dir PATH] [--config-path PATH] plan status ` shows APPLIED phase - - [ ] Query database directly to verify all state transitions recorded - - [ ] Commit: "test(robot): add full lifecycle persistence e2e test" - - [ ] **A5.11b** [Rui] Test: Restart persistence - - [ ] Create plan via CLI - - [ ] Get plan_id from output - - [ ] Simulate process crash (kill the process or restart CLI) - - [ ] Run new CLI command: `agents [--data-dir PATH] [--config-path PATH] plan status ` - - [ ] Verify plan still exists and shows correct state - - [ ] Commit: "test(robot): add restart persistence e2e test" - - [ ] **A5.11c** [Rui] Test: Concurrent CLI access - - [ ] Start two CLI processes accessing same plan - - [ ] One process starts execute, other queries status - - [ ] Verify no data corruption or deadlocks - - [ ] Both processes complete successfully - - [ ] Commit: "test(robot): add concurrent CLI access e2e test" +**Parallel Group A5: Plan Persistence (M1-critical)** + **PARALLEL SUBTRACK A5.alpha [Jeff]**: Alembic migrations for action/plan tables + **PARALLEL SUBTRACK A5.beta [Luis]**: SQLAlchemy models for new tables + **SEQUENTIAL AFTER alpha+beta [Jeff + Luis]**: Repositories + service integration + **PARALLEL CONTINUOUS [Rui]**: Persistence tests added inside each commit + - [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add actions and action_invariants tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add Alembic migration for `actions` table with ULID PK, namespaced_name, actor refs, DoD fields, automation_profile, invariant_actor, timestamps. + - [ ] Code [Jeff]: Add `action_invariants` table with FK to actions, `scope` column, and created_at timestamp. + - [ ] Code [Jeff]: Add unique index on actions.namespaced_name and search index on namespace for list filtering. + - [ ] Code [Jeff]: Ensure downgrade path drops indexes and tables in reverse order. + - [ ] Docs [Jeff]: Update `docs/reference/database_schema.md` with column-level details and constraints. + - [ ] Tests (Behave) [Rui]: Add migration scenario that runs upgrade and asserts tables + indexes exist. + - [ ] Tests (Robot) [Rui]: Add Robot migration smoke test using `nox -s db_migrate` (create session if missing). + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_actions_bench.py` for migration runtime baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(db): add actions and action_invariants tables"`. + - [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add lifecycle_plans and plan_projects tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add Alembic migration for `lifecycle_plans` with ULID PK, phase/state enums, action linkage, automation_profile, invariant_actor, and timestamps. + - [ ] Code [Jeff]: Add `plan_projects` table with plan_id, project_name (namespaced), read_only flag, and alias. + - [ ] Code [Jeff]: Add indexes on plan phase/state for filtering and plan_projects.project_name for lookups. + - [ ] Docs [Jeff]: Update schema docs with plan/project link rules and uniqueness constraints. + - [ ] Tests (Behave) [Rui]: Add migration scenario verifying plan/project link table + indexes. + - [ ] Tests (Robot) [Rui]: Add Robot test that inserts a plan/project link row and queries it. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_plans_bench.py` for migration runtime baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(db): add lifecycle_plans and plan_projects tables"`. + - [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add plan arguments and plan invariants tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add `plan_arguments` table (plan_id, name, value_json, value_type) and `plan_invariants` table (plan_id, invariant_text, source_scope). + - [ ] Code [Jeff]: Add uniqueness constraint on (plan_id, name) for arguments and (plan_id, invariant_text) for invariants. + - [ ] Docs [Jeff]: Document argument storage, JSON serialization rules, and invariant source scopes. + - [ ] Tests (Behave) [Rui]: Add migration scenario verifying both tables and constraints. + - [ ] Tests (Robot) [Rui]: Add Robot test that inserts a plan invariant and asserts retrieval. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_plan_args_bench.py` for migration runtime baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(db): add plan arguments and plan invariants tables"`. + - [ ] **COMMIT (Owner: Luis | Group: A5.beta) - Commit message: "feat(models): add action and lifecycle plan ORM models"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add SQLAlchemy models for Action, ActionInvariant, LifecyclePlan, PlanProjectLink, PlanArgument, PlanInvariant. + - [ ] Code [Luis]: Implement `to_domain()` and `from_domain()` mappings with ULID validation, enum conversion, and timestamp normalization. + - [ ] Code [Luis]: Add repository-facing helpers for filtering by namespace/phase/state. + - [ ] Docs [Luis]: Update ORM mapping notes in `docs/reference/database_schema.md` with model field mapping table. + - [ ] Tests (Behave) [Rui]: Add scenarios for ORM round-trip serialization and enum conversions. + - [ ] Tests (Robot) [Rui]: Add Robot test that loads a plan and asserts field mapping correctness. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/orm_mapping_bench.py` for Action/Plan mapping throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(models): add action and lifecycle plan ORM models"`. + - [ ] **COMMIT (Owner: Jeff | Group: A5.gamma) - Commit message: "feat(repo): add action and lifecycle plan repositories"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Implement ActionRepository CRUD + list filters by namespace/state/automation profile. + - [ ] Code [Jeff]: Implement PlanRepository CRUD + list filters by phase/state/project; add plan lookup by namespaced name. + - [ ] Code [Jeff]: Add retry decorator to repositories; retry only on `OperationalError`, never on `IntegrityError`. + - [ ] Docs [Jeff]: Document repository interfaces, error types, and pagination guidance. + - [ ] Tests (Behave) [Rui]: Add scenarios for repository create/get/list/update/delete guardrails. + - [ ] Tests (Robot) [Rui]: Add Robot test that exercises repository through service layer. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/repository_query_bench.py` for list + filter performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(repo): add action and lifecycle plan repositories"`. + - [ ] **COMMIT (Owner: Luis | Group: A5.gamma) - Commit message: "feat(service): persist plan lifecycle via repositories"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Update `PlanLifecycleService` to use repositories instead of in-memory dicts. + - [ ] Code [Luis]: Ensure plan creation persists arguments, invariants, automation profile, and project links in a single transaction. + - [ ] Code [Luis]: Add transactional safeguards for multi-step updates (create action + plan, correction updates). + - [ ] Docs [Luis]: Update service docs to reflect persistence and remove in-memory notes. + - [ ] Tests (Behave) [Rui]: Add scenarios for persisted lifecycle transitions and error handling. + - [ ] Tests (Robot) [Rui]: Add end-to-end test that restarts the app and re-reads plan state. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_lifecycle_service_bench.py` for persistence operations. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(service): persist plan lifecycle via repositories"`. + - [ ] **COMMIT (Owner: Luis | Group: A5.gamma) - Commit message: "feat(di): wire lifecycle repos and services"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Register ActionRepository + PlanRepository in `application/container.py` and UnitOfWork. + - [ ] Code [Luis]: Inject repositories into PlanLifecycleService and CLI commands. + - [ ] Code [Luis]: Add container wiring tests to ensure singleton lifetimes are correct. + - [ ] Docs [Luis]: Update DI wiring notes in `docs/architecture/decisions/adr-003.md`. + - [ ] Tests (Behave) [Rui]: Add scenarios that use container wiring for lifecycle commands. + - [ ] Tests (Robot) [Rui]: Add Robot smoke test verifying CLI uses persisted service. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/di_container_bench.py` for container resolution overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(di): wire lifecycle repos and services"`. -- [ ] **Stage A6: Automation Levels Foundation** (Day 4-5) **[Luis]** - - [ ] Code: Implement basic automation level support - - [ ] **A6.1** [Luis] Add `AutomationLevel` enum to `src/cleveragents/domain/models/core/plan.py`: - - [ ] Value `MANUAL` - user triggers each phase transition - - [ ] Value `REVIEW_BEFORE_APPLY` - auto strategize+execute, pause before apply - - [ ] Value `FULL_AUTOMATION` - all phases automatic - - [ ] **A6.2** [Luis] Add automation level configuration to `src/cleveragents/config/settings.py`: - - [ ] Add `default_automation_level: AutomationLevel` setting - - [ ] Add `CLEVERAGENTS_AUTOMATION_LEVEL` environment variable - - [ ] Implement hierarchy: plan-level > session-level > global-level - - [ ] **A6.3** [Luis] Update `PlanLifecycleService` to respect automation levels: - - [ ] Add `automation_level` parameter to `use_action()` method - - [ ] If automation allows, automatically call `execute_plan()` after strategize completes - - [ ] If full automation, automatically call `apply_plan()` after execute completes - - [ ] Add pause/resume capability for review-before-apply mode - - [ ] **A6.4** [Luis] Update CLI commands to support automation levels: - - [ ] Add `--automation-level` flag to `agents [--data-dir PATH] [--config-path PATH] plan use` command - - [ ] Add `agents [--data-dir PATH] [--config-path PATH] config set automation-level ` command - - [ ] Add `agents [--data-dir PATH] [--config-path PATH] plan set-automation-level ` command: - - [ ] Can change automation level for existing plan - - [ ] Only affects future phase transitions - - [ ] Subplans created after change use new level - - [ ] Add `agents [--data-dir PATH] [--config-path PATH] session set automation-level ` command: - - [ ] Set session-level automation (overrides global) - - [ ] Persists for current session only - - [ ] Tests: Automation level tests - - [ ] **A6.5** [Rui] Write Behave scenarios in `features/automation_levels.feature`: - - [ ] Scenario: Manual mode requires explicit execute command - - [ ] Scenario: Review-before-apply auto-executes but pauses at apply - - [ ] Scenario: Full automation runs all phases without user input - - [ ] Scenario: Plan-level automation overrides global setting - - [ ] Scenario: Change automation level mid-plan works correctly + - [ ] **COMMIT (Owner: Rui | Group: A5.tests) - Commit message: "test(persistence): add plan/action persistence suites"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Tests (Behave) [Rui]: Add plan persistence scenarios (create, update phase/state, list filters, plan tree, concurrency). + - [ ] Tests (Behave) [Rui]: Add action persistence scenarios (create, list available, archive guard). + - [ ] Tests (Robot) [Rui]: Add plan persistence E2E (full lifecycle, restart persistence, concurrent CLI access). + - [ ] Docs [Rui]: Update `docs/development/testing.md` with persistence suites and `nox` commands. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/persistence_suites_bench.py` for DB read/write baselines. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "test(persistence): add plan/action persistence suites"`. -**M1 SUCCESS CRITERIA**: -- [ ] Can create an action via CLI and it persists to database -- [ ] Can use an action on a project to create a plan -- [ ] Plan transitions through phases with database persistence -- [ ] Automation levels work (at least manual mode fully functional) +**Parallel Group A5.legacy: Remove legacy plan build/apply path (M1-critical)** + - [ ] **COMMIT (Owner: Jeff | Group: A5.legacy) - Commit message: "refactor(plan): remove legacy plan service and CLI"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Remove `PlanService` usage from CLI (`plan tell/build/apply/new/current/list/cd/continue`). + - [ ] Code [Jeff]: Remove or quarantine legacy `plan_service.py`, `plan_legacy.py`, and legacy CLI helpers; add explicit NotImplementedError where needed. + - [ ] Code [Jeff]: Remove or archive legacy `PlanModel`/`PlanStatus` DB tables if unused by v3; document migration path. + - [ ] Docs [Jeff]: Update CLI docs to list only v3 lifecycle commands and new `plan use/execute/apply` flows. + - [ ] Tests (Behave) [Rui]: Remove/replace legacy scenarios with v3 equivalents and adjust coverage expectations. + - [ ] Tests (Robot) [Rui]: Remove legacy robot suites and add v3 replacements where needed. + - [ ] Tests (ASV) [Rui]: Update asv suite to remove legacy plan benchmarks and add v3 lifecycle baseline benchmark. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "refactor(plan): remove legacy plan service and CLI"`. + +**Parallel Group A6: Automation Profiles Foundation [Jeff + Luis]** (M1-critical; depends on A5 persistence) + **PARALLEL SUBTRACK A6.core [Jeff]**: Profile model + built-ins + schema + **PARALLEL SUBTRACK A6.service [Luis]**: Profile resolution + precedence + **PARALLEL SUBTRACK A6.cli [Rui]**: CLI commands for profiles + **SEQUENTIAL MERGE NOTE**: A6.core must land before A6.service/cli; A6.service must land before gating integration in Section 6. + - [ ] **COMMIT (Owner: Jeff | Group: A6.core) - Commit message: "feat(domain): add automation profile model and built-ins"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add `AutomationProfile` model with threshold fields (phase transitions, decision autonomy, child plan spawn, self-repair, apply gating). + - [ ] Code [Jeff]: Add built-in profiles (`manual`, `review`, `supervised`, `full-auto`, etc.) per spec with constant definitions. + - [ ] Code [Jeff]: Add YAML schema for automation profiles under `docs/schema/automation_profile.schema.yaml` and loader helper. + - [ ] Docs [Jeff]: Add `docs/reference/automation_profiles.md` describing built-ins and threshold semantics. + - [ ] Tests (Behave) [Rui]: Add scenarios for profile validation and built-in defaults. + - [ ] Tests (Robot) [Rui]: Add Robot test that loads each built-in profile and prints summary. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_bench.py` for profile validation. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(domain): add automation profile model and built-ins"`. + - [ ] **COMMIT (Owner: Luis | Group: A6.service) - Commit message: "feat(service): resolve automation profiles with precedence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add `AutomationProfileService` to resolve profiles with precedence (plan > action > project > global). + - [ ] Code [Luis]: Add persistence table `automation_profiles` (namespaced name PK) and repository with list/show. + - [ ] Code [Luis]: Add config key `core.automation_profile` and env var override for global default. + - [ ] Docs [Luis]: Update `docs/reference/config.md` with automation profile defaults and override behavior. + - [ ] Tests (Behave) [Rui]: Add scenarios for precedence resolution and missing profile errors. + - [ ] Tests (Robot) [Rui]: Add Robot config smoke test for global profile override. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_resolution_bench.py` for resolution latency. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(service): resolve automation profiles with precedence"`. + - [ ] **COMMIT (Owner: Rui | Group: A6.cli) - Commit message: "feat(cli): add automation-profile commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Rui]: Implement `agents automation-profile add/remove/list/show` commands with YAML config input. + - [ ] Code [Rui]: Add `--automation-profile` to `plan use` and output profile in `plan status`. + - [ ] Docs [Rui]: Update CLI reference with automation-profile command examples. + - [ ] Tests (Behave) [Rui]: Add CLI scenarios for profile add/list/show/remove. + - [ ] Tests (Robot) [Rui]: Add Robot CLI tests for automation-profile commands. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_cli_bench.py` for CLI parsing. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "feat(cli): add automation-profile commands"`. + +**M1 SUCCESS CRITERIA (Day 7 MVP - source code only)**: +- Action created from YAML config and persisted (namespaced name, invariants, automation profile). +- Project created and linked to a local git-checkout resource. +- Plan use -> strategize -> execute -> apply completes with sandbox isolation and diff review. +- Tool-based change tracking produces a ChangeSet and applies to the repo after approval. +- `nox` passes with coverage >=97% on the MVP end-to-end path. --- @@ -1885,29 +1560,174 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Target: Milestone M2 (+10 days)** -**WEEK 1-2 - PARALLEL WITH PLAN LIFECYCLE** +**Parallel Group B1: Resource Registry Core [Hamza + Jeff]** (can start after A5.alpha migrations are available) +- [ ] **COMMIT (Owner: Hamza | Group: B1.core) - Commit message: "feat(domain): add resource type spec and resource model"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Create `src/cleveragents/domain/models/core/resource_type.py` with `ResourceTypeSpec`, `ResourceTypeArgument`, `ResourceKind` (physical/virtual), and `SandboxStrategy` enum. + - [ ] Code [Hamza]: Add resource type fields: `user_addable`, `allowed_parents`, `allowed_children`, `auto_discover`, `handler` reference, and `sandbox_strategy` default. + - [ ] Code [Hamza]: Add `Resource` and `ResourceRef` models with ULID, optional namespaced name, type name, location, description, sandbox strategy, read_only, and metadata. + - [ ] Code [Hamza]: Add validators for namespaced naming, ULID format, and parent/child DAG sanity (no self loops, no duplicate edges, type compatibility). + - [ ] Docs [Hamza]: Add `docs/reference/resource_model.md` with examples for git-checkout and fs-directory resources plus physical/virtual notes. + - [ ] Tests (Behave) [Rui]: Add scenarios validating ULID format, namespace rules, allowed parent/child type checks, and sandbox strategy defaults. + - [ ] Tests (Robot) [Rui]: Add Robot test that loads a ResourceTypeSpec YAML fixture and validates it. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_model_bench.py` for resource validation + DAG checks. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(domain): add resource type spec and resource model"`. +- [ ] **COMMIT (Owner: Hamza | Group: B1.core) - Commit message: "feat(domain): add project model v3 with linked resources"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Add `Project`, `ProjectResourceLink`, `ProjectValidation`, and `ProjectContextPolicy` models using namespaced name as the unique identifier (no ULID per spec). + - [ ] Code [Hamza]: Add fields for `invariants`, `invariant_actor`, `automation_profile`, and `context_views` (strategize/execute/apply/default). + - [ ] Code [Hamza]: Add validation for resource link overrides (read_only flags, alias uniqueness, resource existence). + - [ ] Docs [Hamza]: Add `docs/reference/project_model.md` describing resource linking, validation attachments, and context view policies. + - [ ] Tests (Behave) [Rui]: Add scenarios for project model validation, link overrides, and context view inheritance. + - [ ] Tests (Robot) [Rui]: Add Robot test that creates a Project object and prints serialized output. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_model_bench.py` for serialization/validation performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(domain): add project model v3 with linked resources"`. +- [ ] **COMMIT (Owner: Jeff | Group: B1.core) - Commit message: "feat(db): add resource registry tables"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Add Alembic migration for `resource_types`, `resources`, and `resource_edges` tables with indexes on type/name/namespace. + - [ ] Code [Jeff]: Store `resource_kind` (physical/virtual), `sandbox_strategy`, and optional `namespaced_name` in `resources`. + - [ ] Code [Jeff]: Add foreign keys and cascade rules for resource_edges (parent/child) with uniqueness constraint. + - [ ] Docs [Jeff]: Update `docs/reference/database_schema.md` with resource registry tables and constraints. + - [ ] Tests (Behave) [Rui]: Add migration scenarios verifying tables, indices, and edge uniqueness. + - [ ] Tests (Robot) [Rui]: Add Robot migration smoke test using `nox -s db_migrate`. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_registry_migration_bench.py` for migration baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(db): add resource registry tables"`. -``` -WORKSTREAM B PARALLEL STRUCTURE: +**Parallel Group B2: Project Persistence + Services [Hamza + Luis]** (depends on B1 domain models) +- [ ] **COMMIT (Owner: Jeff | Group: B2.persistence) - Commit message: "feat(db): add projects and project links tables"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Add Alembic migration for `projects`, `project_resource_links`, and `project_validations` tables. + - [ ] Code [Jeff]: Use namespaced name as project primary key; enforce unique constraint on `projects.namespaced_name`. + - [ ] Code [Jeff]: Add indexes for `project_resource_links.project_name` and `resource_id` for fast joins. + - [ ] Docs [Jeff]: Document project table schema and link semantics in `docs/reference/database_schema.md`. + - [ ] Tests (Behave) [Rui]: Add migration scenarios verifying project tables and constraints. + - [ ] Tests (Robot) [Rui]: Add Robot test that inserts a project and link row. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_migration_bench.py` for migration baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(db): add projects and project links tables"`. +- [ ] **COMMIT (Owner: Hamza | Group: B2.persistence) - Commit message: "feat(repo): add resource repositories"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement `ResourceTypeRepository` CRUD and `ResourceRepository` CRUD with DAG edge helpers. + - [ ] Code [Hamza]: Add methods for tree traversal, child discovery queries, and name/ULID resolution. + - [ ] Code [Hamza]: Add repository guardrails for preventing cycles and duplicate edges. + - [ ] Docs [Hamza]: Document repository interfaces in `docs/reference/repositories.md`. + - [ ] Tests (Behave) [Rui]: Add repository scenarios for create/get/list/tree and cycle rejection. + - [ ] Tests (Robot) [Rui]: Add Robot test exercising tree output ordering. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_repository_bench.py` for tree query performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(repo): add resource repositories"`. +- [ ] **COMMIT (Owner: Hamza | Group: B2.persistence) - Commit message: "feat(repo): add project repositories"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement `ProjectRepository` and `ProjectResourceLinkRepository` with namespace filtering and name-based lookup. + - [ ] Code [Hamza]: Add methods to list project validations and context policies. + - [ ] Docs [Hamza]: Update repository docs with project link examples and validation attachment notes. + - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/unlink and validation list. + - [ ] Tests (Robot) [Rui]: Add Robot test that links two resources to one project. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_repository_bench.py` for link/unlink performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(repo): add project repositories"`. +- [ ] **COMMIT (Owner: Hamza | Group: B2.service) - Commit message: "feat(service): add resource registry service"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement `ResourceRegistryService` for register/remove/show/tree operations with name/ULID resolution. + - [ ] Code [Hamza]: Add auto-discovery hook that delegates to resource handlers (git-checkout for MVP). + - [ ] Code [Hamza]: Add validation that resource type supports parent/child linkage before linking. + - [ ] Docs [Hamza]: Add `docs/reference/resource_registry.md` describing API behavior and error cases. + - [ ] Tests (Behave) [Rui]: Add scenarios for register/remove/show/tree behavior and auto-discovery. + - [ ] Tests (Robot) [Rui]: Add Robot test that registers a git-checkout and inspects child count. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_registry_service_bench.py` for register/show performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(service): add resource registry service"`. +- [ ] **COMMIT (Owner: Luis | Group: B2.service) - Commit message: "feat(service): add project service v3"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Implement `ProjectService` create/list/show/delete/link/unlink methods using repositories. + - [ ] Code [Luis]: Add validation attachment helpers and context policy setters for project views. + - [ ] Code [Luis]: Enforce read-only resource links and project-level invariant actor defaults. + - [ ] Docs [Luis]: Update `docs/reference/project_service.md` with usage examples and error cases. + - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/unlink/validation/context policy. + - [ ] Tests (Robot) [Rui]: Add Robot test that creates project and links a resource. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_service_bench.py` for link/unlink performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(service): add project service v3"`. -TRACK B.alpha [Hamza - Day 1-2]: Domain Models (B1.1-B1.6) - └── Can start immediately, no dependencies - -TRACK B.beta [Hamza - Day 2-3]: CLI Commands (B2.1-B2.2) - └── Depends on B1 models - -TRACK B.gamma [Hamza + Luis - Day 3-5]: Sandbox Framework (B3.1-B3.8) - └── Depends on B1 Resource model - └── Luis owns Protocol (B3.1-B3.2), Hamza owns Implementations (B3.3-B3.4) - -TRACK B.delta [Hamza - Day 5-6]: Resource Service (B4.1-B4.3) - └── Depends on B3 sandbox - -TRACK B.epsilon [Hamza - Day 6-7]: Persistence (B5.1-B5.5) - └── Depends on B1 models, parallel with B4 +**Parallel Group B3: CLI Commands [Rui]** (depends on B2 services) +- [ ] **COMMIT (Owner: Rui | Group: B3.cli) - Commit message: "feat(cli): add resource type commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Rui]: Add `agents resource type add/remove/list/show` commands with YAML config input and schema validation. + - [ ] Code [Rui]: Implement `--update` behavior and error on name conflicts per spec. + - [ ] Docs [Rui]: Update CLI reference with resource type examples and expected output columns. + - [ ] Tests (Behave) [Rui]: Add scenarios for resource type lifecycle and invalid schema handling. + - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/resource_type_cli.robot`. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_type_cli_bench.py` for config parsing overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "feat(cli): add resource type commands"`. +- [ ] **COMMIT (Owner: Rui | Group: B3.cli) - Commit message: "feat(cli): add resource commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Rui]: Add `agents resource add/remove/list/show/tree` commands with type-specific flags and name/ULID resolution. + - [ ] Code [Rui]: Implement `resource inspect --tree/--file` per spec for resource introspection. + - [ ] Docs [Rui]: Update CLI reference with resource examples (git-checkout, fs-directory) and output columns. + - [ ] Tests (Behave) [Rui]: Add scenarios for resource registration, list filters, and tree rendering. + - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/resource_cli.robot`. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_cli_bench.py` for command parsing and list output. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "feat(cli): add resource commands"`. +- [ ] **COMMIT (Owner: Rui | Group: B3.cli) - Commit message: "feat(cli): add project commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Rui]: Add `agents project create/show/list/delete/link-resource/unlink-resource` commands using namespaced project names. + - [ ] Code [Rui]: Add `agents project validation add/remove/list` and `project context set/show` commands (context views per phase). + - [ ] Docs [Rui]: Update CLI reference with project examples and validation output. + - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/validation/context policies. + - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/project_cli.robot`. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_cli_bench.py` for command parsing and list output. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "feat(cli): add project commands"`. -TESTING [Rui - Continuous]: Write tests BEFORE implementation -``` +**Parallel Group B4: Sandboxing [Luis + Jeff]** (depends on resource registry + project links) +- [ ] **COMMIT (Owner: Luis | Group: B4.sandbox) - Commit message: "feat(sandbox): add sandbox strategy interface and manager"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Add `SandboxStrategy` protocol, `SandboxRef`, `SandboxManager`, and `SandboxRegistry` with per-resource sandboxes. + - [ ] Code [Luis]: Implement lazy sandbox creation, cleanup hooks, and plan-scoped retention policy stubs. + - [ ] Code [Luis]: Add sandbox path rewriting helper for tool execution and MCP adapters. + - [ ] Docs [Luis]: Add `docs/reference/sandbox.md` describing lifecycle, APIs, and path rewriting rules. + - [ ] Tests (Behave) [Rui]: Add scenarios for sandbox manager creation, cleanup, and path rewrite behavior. + - [ ] Tests (Robot) [Rui]: Add Robot test that creates a sandbox and verifies filesystem isolation. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/sandbox_manager_bench.py` for sandbox creation overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(sandbox): add sandbox strategy interface and manager"`. +- [ ] **COMMIT (Owner: Luis | Group: B4.sandbox) - Commit message: "feat(sandbox): implement git_worktree strategy"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Implement git worktree creation, checkout, and cleanup for git-checkout resources. + - [ ] Code [Luis]: Add safe fallback for repositories without clean worktrees and clear error messages. + - [ ] Code [Luis]: Record sandbox metadata (worktree path, branch, base commit) for rollback. + - [ ] Docs [Luis]: Update sandbox doc with git_worktree usage and rollback behavior. + - [ ] Tests (Behave) [Rui]: Add scenarios for git worktree sandbox creation and rollback. + - [ ] Tests (Robot) [Rui]: Add Robot test that modifies sandbox and verifies original repo unchanged. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/git_worktree_bench.py` for sandbox creation time. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(sandbox): implement git_worktree strategy"`. +- [ ] **COMMIT (Owner: Hamza | Group: B4.sandbox) - Commit message: "feat(resource): add git-checkout handler and discovery"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Add git-checkout handler that validates repo path, branch, and read_only flags. + - [ ] Code [Hamza]: Implement child resource discovery for fs-directory children (schema-only for now) and record ULID-only children. + - [ ] Code [Hamza]: Add sandbox strategy mapping for git-checkout and path normalization helpers. + - [ ] Docs [Hamza]: Document git-checkout handler behavior in `docs/reference/resources_git.md`. + - [ ] Tests (Behave) [Rui]: Add scenarios for handler validation and discovery counts. + - [ ] Tests (Robot) [Rui]: Add Robot test registering a git repo and asserting discovered children. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/git_discovery_bench.py` for discovery cost. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(resource): add git-checkout handler and discovery"`. +- [ ] **COMMIT (Owner: Luis | Group: B4.sandbox) - Commit message: "feat(sandbox): add copy_on_write strategy stub"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Add copy_on_write strategy skeleton with TODOs for large-project optimization. + - [ ] Code [Luis]: Raise explicit NotImplementedError with guidance on when it will be available. + - [ ] Docs [Luis]: Document that copy_on_write is stubbed for post-M1 work. + - [ ] Tests (Behave) [Rui]: Add scenario that selecting copy_on_write raises NotImplementedError with clear message. + - [ ] Tests (Robot) [Rui]: Add Robot test verifying stub error output. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/sandbox_stub_bench.py` (baseline no-op). + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(sandbox): add copy_on_write strategy stub"`. - [ ] **Stage B1: Project Data Model** (Day 1-2) **[Hamza - Python Expert, RDF Background]** @@ -3476,1343 +3296,273 @@ TESTING [Rui - Continuous]: Write tests BEFORE implementation **WEEK 2 - CRITICAL FOR MVP** -- [ ] **Stage C1: Actor YAML Schema Formalization** (Day 5-6) **[Aditya - Domain Expert]** - - **SEQUENTIAL ORDER**: C1.1 (Enums) → C1.2 (Tool/Route models) → C1.3 (Context model) → C1.4 (ActorConfigSchema) → C1.5 (Examples) → C1.6 (Docs) → C1.7 (Tests) - - - [ ] Code: Formalize actor YAML schema - - [ ] **C1.1** [Aditya] Define core enums in `src/cleveragents/actor/schema.py`: - - [ ] **C1.1a** [Aditya] Create file with `ActorType` enum: - ```python - class ActorType(str, Enum): - """Type of actor determining execution behavior.""" - LLM = "llm" # Single LLM with system prompt - TOOL = "tool" # Collection of callable tools - GRAPH = "graph" # Multi-node StateGraph with routing - ``` - - [ ] Commit: "feat(actor): define ActorType enum" - - [ ] **C1.1b** [Aditya] Define `NodeType` enum: - ```python - class NodeType(str, Enum): - """Type of node in a graph actor.""" - AGENT = "agent" # LLM agent node - TOOL = "tool" # Tool execution node - CONDITIONAL = "conditional" # Routing/conditional node - SUBGRAPH = "subgraph" # Nested actor reference - ``` - - [ ] Commit: "feat(actor): define NodeType enum" - - [ ] **C1.1c** [Aditya] Define `ContextView` enum: - ```python - class ContextView(str, Enum): - """Role-based context filtering for actors.""" - STRATEGIST = "strategist" # High-level architecture, READMEs - EXECUTOR = "executor" # Precise code sections for edits - REVIEWER = "reviewer" # Diffs, tests, style guides - FULL = "full" # Complete context (default) - ``` - - [ ] Commit: "feat(actor): define ContextView enum" - - [ ] **C1.2** [Aditya] Define tool and route models: - - [ ] **C1.2a** [Aditya] Define `ToolParameter` model: - ```python - class ToolParameter(BaseModel): - """Parameter definition for inline tool.""" - name: str = Field(..., description="Parameter name") - type: str = Field(..., description="JSON Schema type (string, integer, object, etc.)") - description: str = Field(..., description="What this parameter is for") - required: bool = Field(default=True) - default: Any = Field(default=None) - enum: list[str] | None = Field(default=None, description="Allowed values") - ``` - - [ ] Commit: "feat(actor): define ToolParameter model" - - [ ] **C1.2b** [Aditya] Define `ToolDefinition` model: - ```python - class ToolDefinition(BaseModel): - """Inline tool/skill definition in actor YAML.""" - name: str = Field(..., min_length=1, max_length=100, description="Tool identifier") - description: str = Field(..., description="What the tool does (shown to LLM)") - parameters: list[ToolParameter] = Field(default_factory=list) - returns: str = Field(default="Any", description="Return type documentation") - code: str = Field(..., description="Python code to execute") - timeout_seconds: int = Field(default=30, ge=1, le=300) - - @field_validator('code') - @classmethod - def validate_code_syntax(cls, v: str) -> str: - """Validate Python syntax without executing.""" - try: - compile(v, '', 'exec') - except SyntaxError as e: - raise ValueError(f"Invalid Python syntax: {e}") - return v - ``` - - [ ] Commit: "feat(actor): define ToolDefinition model with code validation" - - [ ] **C1.2c** [Aditya] Define `EdgeDefinition` model: - ```python - class EdgeDefinition(BaseModel): - """Edge in actor graph topology.""" - source: str = Field(..., description="Source node name") - target: str = Field(..., description="Target node name") - condition: str | None = Field(default=None, description="Python expression for conditional routing") - label: str | None = Field(default=None, description="Edge label for visualization") - ``` - - [ ] Commit: "feat(actor): define EdgeDefinition model" - - [ ] **C1.2d** [Aditya] Define `NodeDefinition` model: - ```python - class NodeDefinition(BaseModel): - """Node in actor graph.""" - name: str = Field(..., description="Unique node identifier") - type: NodeType = Field(..., description="Type of node") - # For agent nodes: - model: str | None = Field(default=None, description="Model name for agent nodes") - system_prompt: str | None = Field(default=None) - tools: list[str] | None = Field(default=None, description="Tool names available to this agent") - # For tool nodes: - tool: str | None = Field(default=None, description="Tool name to execute") - # For subgraph nodes: - actor: str | None = Field(default=None, description="Actor reference (e.g., local/other-actor)") - ``` - - [ ] Commit: "feat(actor): define NodeDefinition model" - - [ ] **C1.2e** [Aditya] Define `RouteDefinition` model: - ```python - class RouteDefinition(BaseModel): - """Complete graph topology definition.""" - nodes: list[NodeDefinition] = Field(..., min_length=1) - edges: list[EdgeDefinition] = Field(default_factory=list) - entry_point: str = Field(..., description="Name of starting node") - - @model_validator(mode='after') - def validate_topology(self) -> Self: - """Validate graph is well-formed.""" - node_names = {n.name for n in self.nodes} - if self.entry_point not in node_names: - raise ValueError(f"entry_point '{self.entry_point}' not in nodes") - for edge in self.edges: - if edge.source not in node_names: - raise ValueError(f"Edge source '{edge.source}' not in nodes") - if edge.target not in node_names: - raise ValueError(f"Edge target '{edge.target}' not in nodes") - return self - ``` - - [ ] Commit: "feat(actor): define RouteDefinition with topology validation" - - [ ] **C1.3** [Aditya] Define context/memory configuration: - - [ ] **C1.3a** [Aditya] Define `MemoryConfig` model: - ```python - class MemoryConfig(BaseModel): - """Memory/conversation history settings.""" - enabled: bool = Field(default=True, description="Whether to maintain history") - max_turns: int = Field(default=20, ge=1, le=100, description="Max conversation turns") - summarization_threshold: int = Field(default=15, description="Turns before summarizing") - include_system_messages: bool = Field(default=True) - ``` - - [ ] Commit: "feat(actor): define MemoryConfig model" - - [ ] **C1.3b** [Aditya] Define `ContextConfigSchema` model: - ```python - class ContextConfigSchema(BaseModel): - """Context window configuration for actor.""" - context_window_fraction: float = Field(default=0.8, ge=0.1, le=1.0, - description="Fraction of model's context window to use") - context_view: ContextView = Field(default=ContextView.FULL, - description="Role-based context filtering") - include_file_patterns: list[str] = Field(default_factory=list, - description="Glob patterns for files to always include") - exclude_file_patterns: list[str] = Field(default_factory=list, - description="Glob patterns for files to never include") - max_file_size_kb: int = Field(default=100, description="Max file size to include") - ``` - - [ ] Commit: "feat(actor): define ContextConfigSchema model" - - [ ] **C1.4** [Aditya] Define main `ActorConfigSchema`: - - [ ] **C1.4a** [Aditya] Create comprehensive model: - ```python - class ActorConfigSchema(BaseModel): - """Complete actor configuration from YAML.""" - # Identity - version: str = Field(default="3", description="Config schema version") - name: str = Field(..., description="Actor name (without namespace)") - namespace: str = Field(default="local") - description: str | None = Field(default=None) - tags: list[str] = Field(default_factory=list) - - # Type and provider - type: ActorType = Field(..., description="Actor type") - model: str | None = Field(default=None, description="LLM model name") - provider: str | None = Field(default=None, description="Provider: openai, anthropic, etc.") - - # LLM configuration - system_prompt: str | None = Field(default=None) - temperature: float = Field(default=0.7, ge=0.0, le=2.0) - max_tokens: int | None = Field(default=None) - - # Tools/skills - tools: list[ToolDefinition] = Field(default_factory=list, - description="Inline tool definitions") - builtin_tools: list[str] = Field(default_factory=list, - description="Names of built-in tools to include") - mcp_servers: list[str] = Field(default_factory=list, - description="MCP server identifiers to connect") - - # Graph topology (for type=GRAPH) - routes: RouteDefinition | None = Field(default=None) - - # Memory and context - memory: MemoryConfig = Field(default_factory=MemoryConfig) - context: ContextConfigSchema = Field(default_factory=ContextConfigSchema) - - # Execution - timeout_seconds: int = Field(default=300, description="Total execution timeout") - max_iterations: int = Field(default=50, description="Max LLM calls per invocation") - - @model_validator(mode='after') - def validate_type_requirements(self) -> Self: - """Validate fields based on actor type.""" - if self.type == ActorType.LLM: - if not self.model: - raise ValueError("LLM actors require 'model' field") - if self.type == ActorType.GRAPH: - if not self.routes: - raise ValueError("GRAPH actors require 'routes' field") - return self - - model_config = ConfigDict(extra='forbid') # Reject unknown fields - ``` - - [ ] Commit: "feat(actor): define ActorConfigSchema with validation" - - [ ] **C1.4b** [Aditya] Add YAML loading helper: - ```python - @classmethod - def from_yaml(cls, path: Path | str) -> "ActorConfigSchema": - """Load and validate actor config from YAML file.""" - import yaml - with open(path) as f: - data = yaml.safe_load(f) - return cls.model_validate(data) - - def to_yaml(self) -> str: - """Serialize config to YAML string.""" - import yaml - return yaml.dump(self.model_dump(exclude_none=True), sort_keys=False) - ``` - - [ ] Commit: "feat(actor): add YAML serialization helpers" - - [ ] **C1.5** [Aditya] Create comprehensive example actors in `examples/actors/`: - - [ ] **C1.5a** [Aditya] Create `simple_llm_actor.yaml`: - ```yaml - version: "3" - name: simple-assistant - namespace: local - description: Basic LLM assistant with no tools - type: llm - model: gpt-4-turbo - provider: openai - system_prompt: | - You are a helpful coding assistant. Answer questions - concisely and provide code examples when appropriate. - temperature: 0.7 - memory: - enabled: true - max_turns: 10 - ``` - - [ ] Commit: "docs(examples): add simple_llm_actor.yaml" - - [ ] **C1.5b** [Aditya] Create `tool_actor.yaml`: - ```yaml - version: "3" - name: file-reader - namespace: local - description: Actor that can read and search files - type: llm - model: gpt-4-turbo - system_prompt: | - You can read and search files to answer questions. - tools: - - name: read_file - description: Read contents of a file - parameters: - - name: path - type: string - description: Path to file - code: | - result = context.get_file(input_data["path"]) - - name: search_files - description: Search for pattern in files - parameters: - - name: pattern - type: string - description: Regex pattern to search - code: | - result = context.search_files("**/*", input_data["pattern"]) - builtin_tools: - - list_directory - ``` - - [ ] Commit: "docs(examples): add tool_actor.yaml" - - [ ] **C1.5c** [Aditya] Create `graph_actor.yaml`: - ```yaml - version: "3" - name: research-writer - namespace: local - description: Multi-step research and writing workflow - type: graph - routes: - entry_point: planner - nodes: - - name: planner - type: agent - model: gpt-4-turbo - system_prompt: Break down the writing task into research topics. - - name: researcher - type: agent - model: gpt-4-turbo - system_prompt: Research the assigned topic thoroughly. - tools: [search_files, read_file] - - name: writer - type: agent - model: gpt-4-turbo - system_prompt: Write content based on research findings. - - name: router - type: conditional - edges: - - source: planner - target: router - - source: router - target: researcher - condition: "state.needs_research" - - source: router - target: writer - condition: "not state.needs_research" - - source: researcher - target: router - ``` - - [ ] Commit: "docs(examples): add graph_actor.yaml" - - [ ] **C1.5d** [Aditya] Create `hierarchical_actor.yaml`: - ```yaml - version: "3" - name: code-reviewer - namespace: local - description: Hierarchical actor that delegates to specialists - type: graph - routes: - entry_point: coordinator - nodes: - - name: coordinator - type: agent - model: gpt-4-turbo - system_prompt: | - Coordinate code review by delegating to specialists. - - name: security-check - type: subgraph - actor: local/security-analyzer - - name: style-check - type: subgraph - actor: local/style-checker - - name: aggregator - type: agent - model: gpt-4-turbo - system_prompt: Combine specialist feedback into final review. - edges: - - source: coordinator - target: security-check - - source: coordinator - target: style-check - - source: security-check - target: aggregator - - source: style-check - target: aggregator - ``` - - [ ] Commit: "docs(examples): add hierarchical_actor.yaml" - - [ ] **C1.5e** [Aditya] Create `strategy_actor.yaml`: - ```yaml - version: "3" - name: default-strategist - namespace: cleveragents - description: Default strategist for Strategize phase - type: llm - model: gpt-4-turbo - temperature: 0.3 # Lower for more consistent strategy - system_prompt: | - You are a technical strategist. Given a task description and codebase context, - create a detailed plan with specific steps. - - Your output must include: - 1. High-level approach explanation - 2. Ordered list of steps with file paths - 3. Dependencies between steps - 4. Risk assessment - 5. Decisions with alternatives considered - - Format decisions as: - DECISION: - CHOSEN: - ALTERNATIVES: - RATIONALE: - context: - context_view: strategist - include_file_patterns: - - "README.md" - - "**/README.md" - - "docs/**/*.md" - ``` - - [ ] Commit: "docs(examples): add strategy_actor.yaml" - - [ ] **C1.5f** [Aditya] Create `execution_actor.yaml`: - ```yaml - version: "3" - name: default-executor - namespace: cleveragents - description: Default executor for Execute phase - type: llm - model: gpt-4-turbo - temperature: 0.2 # Low for precise code generation - system_prompt: | - You are a code executor. Given a strategy and file context, - implement the required changes using the provided tools. - - RULES: - - Use tools to read files before editing - - Use edit_file for targeted changes, write_file for new files - - Always verify changes compile/parse correctly - - Document each change with clear commit messages - tools: - - name: edit_file - description: Make targeted edits to an existing file - parameters: - - name: path - type: string - - name: edits - type: array - code: | - result = context.edit_file(input_data["path"], input_data["edits"]) - builtin_tools: - - read_file - - write_file - - delete_file - - list_directory - - search_files - context: - context_view: executor - max_iterations: 100 # Allow more iterations for complex tasks - ``` - - [ ] Commit: "docs(examples): add execution_actor.yaml" - - [ ] **C1.6** [Aditya] Write comprehensive documentation: - - [ ] **C1.6a** [Aditya] Create `docs/reference/actor_configuration.md`: - - [ ] Full YAML schema reference with all fields - - [ ] Type-specific requirements (LLM vs GRAPH) - - [ ] Tool definition syntax and examples - - [ ] Memory and context configuration - - [ ] Commit: "docs: add actor configuration reference" - - [ ] **C1.6b** [Aditya] Add examples section: - - [ ] Example for each actor type - - [ ] Common patterns (research, review, generation) - - [ ] Anti-patterns to avoid - - [ ] Commit: "docs: add actor configuration examples" - - [ ] **C1.6c** [Aditya] Add migration guide: - - [ ] Changes from v2 format - - [ ] Automated migration script (if needed) - - [ ] Commit: "docs: add actor config migration guide" - - [ ] Tests: Behave scenarios for actor schema validation - - [ ] **C1.7** [Rui] Write Behave scenarios in `features/actor_schema.feature`: - - [ ] **C1.7a** [Rui] Valid config scenarios: - - [ ] Scenario: Simple LLM actor config validates successfully - - [ ] Scenario: Tool actor with inline code validates - - [ ] Scenario: Graph actor with complete topology validates - - [ ] Scenario: Actor with MCP servers configured validates - - [ ] Commit: "test(behave): add valid actor config scenarios" - - [ ] **C1.7b** [Rui] Invalid config scenarios: - - [ ] Scenario: LLM actor without model field fails - - [ ] Scenario: Graph actor without routes fails - - [ ] Scenario: Tool with invalid Python syntax fails - - [ ] Scenario: Graph with missing entry_point fails - - [ ] Scenario: Edge referencing non-existent node fails - - [ ] Commit: "test(behave): add invalid actor config scenarios" - - [ ] **C1.7c** [Rui] YAML loading scenarios: - - [ ] Scenario: Load actor config from YAML file - - [ ] Scenario: Invalid YAML syntax produces clear error - - [ ] Scenario: Unknown fields in YAML are rejected - - [ ] Commit: "test(behave): add YAML loading scenarios" +**Parallel Group C0: Tool Registry + Validation System [Jeff + Luis]** (start Day 5; precedes C1/C3) + **PARALLEL SUBTRACK C0.domain [Jeff]**: Tool + Validation domain models + schemas + **PARALLEL SUBTRACK C0.registry [Luis]**: Tool registry persistence + repositories + **PARALLEL SUBTRACK C0.cli [Rui]**: CLI commands for tools/validations + **SEQUENTIAL MERGE NOTE**: C0.domain must land before C0.registry/cli; C0.registry before C3 context wiring. + - [ ] **COMMIT (Owner: Jeff | Group: C0.domain) - Commit message: "feat(tool): add tool and validation domain models"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add `Tool` model with namespaced name, description, source type, input/output JSON schema, and capability metadata (read/write/checkpointable). + - [ ] Code [Jeff]: Add `ResourceBinding` model with slot definitions and binding modes (context, static, parameter). + - [ ] Code [Jeff]: Add `Validation` model as Tool subtype with `mode`, `wraps`, and `transform` fields; enforce read-only constraints. + - [ ] Code [Jeff]: Add enums for ToolSource, ToolType (tool/validation), and ValidationMode. + - [ ] Docs [Jeff]: Add `docs/reference/tool_model.md` and `docs/reference/validation_model.md` with examples. + - [ ] Tests (Behave) [Rui]: Add `features/tool_model.feature` for schema validation, resource binding rules, and validation constraints. + - [ ] Tests (Robot) [Rui]: Add `robot/tool_model.robot` smoke tests for model creation. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_model_bench.py` for schema validation throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(tool): add tool and validation domain models"`. + - [ ] **COMMIT (Owner: Luis | Group: C0.registry) - Commit message: "feat(tool): add tool registry persistence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add DB tables for `tools`, `tool_bindings`, and `validation_attachments` with indexes on namespaced name and type. + - [ ] Code [Luis]: Implement ToolRepository + ValidationAttachmentRepository with list/show filters. + - [ ] Code [Luis]: Add ToolRegistryService for register/update/remove/list/show with name conflict checks. + - [ ] Docs [Luis]: Update `docs/reference/database_schema.md` with tool/validation tables. + - [ ] Tests (Behave) [Rui]: Add `features/tool_registry.feature` for register/update/remove and validation-only constraints. + - [ ] Tests (Robot) [Rui]: Add `robot/tool_registry.robot` for list/show smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_registry_bench.py` for registry list performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(tool): add tool registry persistence"`. + - [ ] **COMMIT (Owner: Jeff | Group: C0.binding) - Commit message: "feat(tool): add resource binding resolution"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Implement binding resolution for contextual, static, and parameter bindings with type compatibility checks. + - [ ] Code [Jeff]: Add resolution helpers for resource name/ULID lookup and project-scoped filtering. + - [ ] Docs [Jeff]: Add `docs/reference/tool_bindings.md` with resolution order and examples. + - [ ] Tests (Behave) [Rui]: Add binding resolution scenarios (context vs static vs parameter). + - [ ] Tests (Robot) [Rui]: Add Robot test resolving a bound resource by name. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/binding_resolution_bench.py` for resolution latency. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(tool): add resource binding resolution"`. + - [ ] **COMMIT (Owner: Rui | Group: C0.cli) - Commit message: "feat(cli): add tool and validation commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Rui]: Implement `agents tool add/remove/list/show` with YAML config input and `--type` filter. + - [ ] Code [Rui]: Implement `agents validation add/attach/detach` commands and enforce validation-only name use. + - [ ] Docs [Rui]: Update CLI reference with tool/validation commands and output format. + - [ ] Tests (Behave) [Rui]: Add CLI scenarios for tool/validation registration and attachment. + - [ ] Tests (Robot) [Rui]: Add Robot CLI suites for tool and validation commands. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_cli_bench.py` for CLI parsing overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "feat(cli): add tool and validation commands"`. -- [ ] **Stage C2: Actor Loading & Compilation** (Day 6-8) **[Aditya]** - - **SEQUENTIAL ORDER**: C2.1 (Config parser) → C2.2 (CompiledActor) → C2.3 (LLM compiler) → C2.4 (Tool compiler) → C2.5 (Graph compiler) → C2.6 (Reference resolution) → C2.7 (Registry) → C2.8 (Tests) - - - [ ] Code: Enhance actor loading and compilation to LangGraph - - [ ] **C2.1** [Aditya] Create config parser in `src/cleveragents/actor/config.py`: - - [ ] **C2.1a** [Aditya] Define `ActorConfigParser` class: - ```python - class ActorConfigParser: - """Parse and validate actor configurations.""" - - def __init__(self, registry: "ActorRegistry"): - self._registry = registry - - def parse_file(self, path: Path) -> ActorConfigSchema: - """Parse actor config from YAML file.""" - return ActorConfigSchema.from_yaml(path) - - def parse_string(self, content: str) -> ActorConfigSchema: - """Parse actor config from YAML string.""" - import yaml - data = yaml.safe_load(content) - return ActorConfigSchema.model_validate(data) - ``` - - [ ] Commit: "feat(actor): add ActorConfigParser scaffold" - - [ ] **C2.1b** [Aditya] Add tools section validation: - - [ ] Validate each tool has unique name - - [ ] Validate tool code compiles (syntax check) - - [ ] Validate tool parameters have valid JSON Schema types - - [ ] Commit: "feat(actor): add tools section validation" - - [ ] **C2.1c** [Aditya] Add routes section validation: - - [ ] Validate all node names are unique - - [ ] Validate entry_point exists in nodes - - [ ] Validate all edge sources/targets exist - - [ ] Check for unreachable nodes (warning) - - [ ] Commit: "feat(actor): add routes section validation" - - [ ] **C2.1d** [Aditya] Add actor reference validation: - - [ ] For subgraph nodes, validate `actor` field is present - - [ ] Validate referenced actor exists in registry - - [ ] Build dependency graph for circular reference detection - - [ ] Commit: "feat(actor): add actor reference validation" - - [ ] **C2.2** [Aditya] Define `CompiledActor` in `src/cleveragents/actor/compiled.py`: - - [ ] **C2.2a** [Aditya] Create CompiledActor dataclass: - ```python - @dataclass - class CompiledActor: - """A compiled actor ready for execution.""" - config: ActorConfigSchema - graph: StateGraph # LangGraph StateGraph - runnable: CompiledStateGraph # Compiled version for execution - tools: dict[str, Callable] # Name -> callable tool functions - referenced_actors: list[str] # Actor names this depends on - compiled_at: datetime - - def invoke(self, input_data: dict, config: RunnableConfig | None = None) -> dict: - """Execute the actor graph with input.""" - return self.runnable.invoke(input_data, config) - - async def ainvoke(self, input_data: dict, config: RunnableConfig | None = None) -> dict: - """Execute the actor graph asynchronously.""" - return await self.runnable.ainvoke(input_data, config) - ``` - - [ ] Commit: "feat(actor): define CompiledActor dataclass" - - [ ] **C2.3** [Aditya] Create `ActorCompiler` in `src/cleveragents/actor/compiler.py`: - - [ ] **C2.3a** [Aditya] Define compiler class scaffold: - ```python - class ActorCompiler: - """Compile actor configs into executable LangGraph graphs.""" - - def __init__( - self, - model_factory: ModelFactory, - skill_registry: SkillRegistry, - mcp_manager: MCPServerManager | None = None - ): - self._model_factory = model_factory - self._skill_registry = skill_registry - self._mcp_manager = mcp_manager - self._compiled_cache: dict[str, CompiledActor] = {} - ``` - - [ ] Commit: "feat(actor): add ActorCompiler scaffold" - - [ ] **C2.3b** [Aditya] Implement main `compile()` method: - ```python - def compile(self, config: ActorConfigSchema) -> CompiledActor: - """Compile actor config into executable graph.""" - # Check cache - cache_key = f"{config.namespace}/{config.name}" - if cache_key in self._compiled_cache: - return self._compiled_cache[cache_key] - - # Compile based on type - match config.type: - case ActorType.LLM: - graph = self._compile_llm_actor(config) - case ActorType.TOOL: - graph = self._compile_tool_actor(config) - case ActorType.GRAPH: - graph = self._compile_graph_actor(config) - - # Build tools dict - tools = self._build_tools(config) - - # Create CompiledActor - compiled = CompiledActor( - config=config, - graph=graph, - runnable=graph.compile(), - tools=tools, - referenced_actors=self._get_referenced_actors(config), - compiled_at=datetime.utcnow() - ) - - # Cache and return - self._compiled_cache[cache_key] = compiled - return compiled - ``` - - [ ] Commit: "feat(actor): implement ActorCompiler.compile()" - - [ ] **C2.4** [Aditya] Implement LLM actor compilation: - - [ ] **C2.4a** [Aditya] Implement `_compile_llm_actor()`: - ```python - def _compile_llm_actor(self, config: ActorConfigSchema) -> StateGraph: - """Compile simple LLM actor into single-node graph.""" - from langgraph.graph import StateGraph, END - from langchain_core.messages import HumanMessage, SystemMessage - - # Create model - model = self._model_factory.create( - model_name=config.model, - provider=config.provider, - temperature=config.temperature, - max_tokens=config.max_tokens - ) - - # Bind tools if any - tools = self._build_tools(config) - if tools: - model = model.bind_tools(list(tools.values())) - - # Define state - class State(TypedDict): - messages: list[BaseMessage] - context: dict - - # Define agent node - def agent(state: State) -> State: - messages = state["messages"] - if config.system_prompt: - messages = [SystemMessage(content=config.system_prompt)] + messages - response = model.invoke(messages) - return {"messages": [response]} - - # Build graph - graph = StateGraph(State) - graph.add_node("agent", agent) - graph.set_entry_point("agent") - graph.add_edge("agent", END) - - return graph - ``` - - [ ] Commit: "feat(actor): implement LLM actor compilation" - - [ ] **C2.4b** [Aditya] Add memory support to LLM actors: - - [ ] If config.memory.enabled, wrap with memory checkpointer - - [ ] Configure message trimming based on max_turns - - [ ] Commit: "feat(actor): add memory support to LLM actors" - - [ ] **C2.5** [Aditya] Implement tool actor compilation: - - [ ] **C2.5a** [Aditya] Implement `_compile_tool_actor()`: - - [ ] Create ReAct-style agent with tools - - [ ] Configure tool calling loop - - [ ] Add tool nodes for each defined tool - - [ ] Commit: "feat(actor): implement tool actor compilation" - - [ ] **C2.5b** [Aditya] Implement tool node generation: - ```python - def _build_tools(self, config: ActorConfigSchema) -> dict[str, Callable]: - """Build callable tools from config.""" - tools = {} - - # Inline tools from YAML - for tool_def in config.tools: - tools[tool_def.name] = self._create_inline_tool(tool_def) - - # Built-in tools - for tool_name in config.builtin_tools: - tool = self._skill_registry.get_skill(tool_name) - if tool: - tools[tool_name] = tool.to_langchain_tool() - - # MCP tools - if config.mcp_servers and self._mcp_manager: - for server_id in config.mcp_servers: - mcp_tools = self._mcp_manager.get_tools(server_id) - tools.update(mcp_tools) - - return tools - ``` - - [ ] Commit: "feat(actor): implement tool building from config" - - [ ] **C2.6** [Aditya] Implement graph actor compilation: - - [ ] **C2.6a** [Aditya] Implement `_compile_graph_actor()`: - ```python - def _compile_graph_actor(self, config: ActorConfigSchema) -> StateGraph: - """Compile multi-node graph actor.""" - routes = config.routes - - # Define state type dynamically based on nodes - State = self._build_state_type(routes) - - # Create graph - graph = StateGraph(State) - - # Add nodes - for node_def in routes.nodes: - node_func = self._create_node(node_def, config) - graph.add_node(node_def.name, node_func) - - # Set entry point - graph.set_entry_point(routes.entry_point) - - # Add edges - for edge in routes.edges: - if edge.condition: - # Conditional edge - condition_func = self._parse_condition(edge.condition) - graph.add_conditional_edges( - edge.source, - condition_func, - {True: edge.target} - ) - else: - # Direct edge - graph.add_edge(edge.source, edge.target) - - return graph - ``` - - [ ] Commit: "feat(actor): implement graph actor compilation" - - [ ] **C2.6b** [Aditya] Implement node creation by type: - - [ ] Agent nodes: create LLM with optional tools - - [ ] Tool nodes: create tool execution wrapper - - [ ] Conditional nodes: create routing logic - - [ ] Subgraph nodes: compile and embed referenced actor - - [ ] Commit: "feat(actor): implement node creation by type" - - [ ] **C2.7** [Aditya] Implement actor reference resolution: - - [ ] **C2.7a** [Aditya] Add circular reference detection: - ```python - def _check_circular_references( - self, - config: ActorConfigSchema, - visited: set[str] | None = None - ) -> None: - """Detect circular actor references.""" - visited = visited or set() - actor_name = f"{config.namespace}/{config.name}" - - if actor_name in visited: - raise CircularReferenceError( - f"Circular reference detected: {' -> '.join(visited)} -> {actor_name}" - ) - - visited.add(actor_name) - - for ref in self._get_referenced_actors(config): - ref_config = self._registry.get_config(ref) - if ref_config: - self._check_circular_references(ref_config, visited.copy()) - ``` - - [ ] Commit: "feat(actor): add circular reference detection" - - [ ] **C2.7b** [Aditya] Implement recursive compilation: - - [ ] When compiling subgraph node, recursively compile referenced actor - - [ ] Cache compiled actors to avoid recompilation - - [ ] Pass context appropriately to subgraphs - - [ ] Commit: "feat(actor): implement recursive actor compilation" - - [ ] **C2.8** [Aditya] Update `ActorRegistry` to support compilation: - - [ ] **C2.8a** [Aditya] Add `get_compiled()` method: - ```python - def get_compiled(self, name: str) -> CompiledActor: - """Get compiled actor by name, compiling if needed.""" - # Check compiled cache - if name in self._compiled_cache: - cached = self._compiled_cache[name] - # Check if config changed - current_config = self.get_config(name) - if current_config and self._config_unchanged(name, current_config): - return cached - - # Load config - config = self.get_config(name) - if not config: - raise ActorNotFoundError(f"Actor '{name}' not found") - - # Compile - compiled = self._compiler.compile(config) - self._compiled_cache[name] = compiled - - return compiled - ``` - - [ ] Commit: "feat(actor): add ActorRegistry.get_compiled()" - - [ ] **C2.8b** [Aditya] Add cache invalidation: - - [ ] Monitor actor YAML files for changes - - [ ] Clear cache entry when config file modified - - [ ] Clear dependent actors when base actor changes - - [ ] Commit: "feat(actor): add compiled actor cache invalidation" - - [ ] Tests: Behave scenarios for actor compilation - - [ ] **C2.9** [Rui] Write Behave scenarios in `features/actor_compilation.feature`: - - [ ] **C2.9a** [Rui] LLM actor compilation scenarios: - - [ ] Scenario: Compile simple LLM actor creates valid graph - - [ ] Given actor config with type=llm and model=gpt-4 - - [ ] When I compile the actor - - [ ] Then CompiledActor is returned - - [ ] And graph has single agent node - - [ ] And runnable can be invoked - - [ ] Scenario: LLM actor with tools binds tools correctly - - [ ] Commit: "test(behave): add LLM actor compilation scenarios" - - [ ] **C2.9b** [Rui] Tool actor compilation scenarios: - - [ ] Scenario: Compile tool actor with inline code works - - [ ] Given actor config with inline tool definitions - - [ ] When I compile the actor - - [ ] Then tools dict contains the defined tools - - [ ] And tools are callable - - [ ] Scenario: Built-in tools are included - - [ ] Commit: "test(behave): add tool actor compilation scenarios" - - [ ] **C2.9c** [Rui] Graph actor compilation scenarios: - - [ ] Scenario: Compile graph actor creates correct topology - - [ ] Given actor config with routes defining 3 nodes - - [ ] When I compile the actor - - [ ] Then graph has 3 nodes - - [ ] And edges match route definition - - [ ] And entry_point is set correctly - - [ ] Scenario: Conditional edges work correctly - - [ ] Commit: "test(behave): add graph actor compilation scenarios" - - [ ] **C2.9d** [Rui] Reference resolution scenarios: - - [ ] Scenario: Actor referencing other actor compiles recursively - - [ ] Given actor A references actor B as subgraph - - [ ] When I compile actor A - - [ ] Then actor B is also compiled - - [ ] And actor B graph is embedded in actor A - - [ ] Scenario: Circular reference detected and errors - - [ ] Given actor A references B and B references A - - [ ] When I try to compile actor A - - [ ] Then CircularReferenceError is raised - - [ ] And error message shows the cycle - - [ ] Commit: "test(behave): add reference resolution scenarios" - - [ ] **C2.9e** [Rui] Error scenarios: - - [ ] Scenario: Invalid actor config produces clear error - - [ ] Scenario: Missing referenced actor produces clear error - - [ ] Scenario: Invalid tool code produces clear error - - [ ] Commit: "test(behave): add compilation error scenarios" +**Parallel Group C1: Actor Schema & Examples [Aditya + Jeff]** (start Day 5; C2 depends on this) +- [ ] **COMMIT (Owner: Aditya | Group: C1.schema) - Commit message: "feat(actor): add actor yaml schema models"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Aditya]: Add schema models (ActorType, NodeType, ContextView, ToolDefinition, RouteDefinition, ActorConfigSchema) with strict validation. + - [ ] Code [Aditya]: Add tool-node schema fields that reference Tool Registry names, including validation nodes. + - [ ] Code [Aditya]: Add YAML load/serialize helpers and schema version guard. + - [ ] Docs [Aditya]: Add `docs/reference/actors_schema.md` with field definitions, tool node semantics, and graph constraints. + - [ ] Tests (Behave) [Rui]: Add `features/actor_schema.feature` scenarios for validation and topology errors. + - [ ] Tests (Robot) [Rui]: Add `robot/actor_schema.robot` YAML load smoke test. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_schema_bench.py` for YAML validation cost. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Aditya]: `git commit -m "feat(actor): add actor yaml schema models"`. +- [ ] **COMMIT (Owner: Aditya | Group: C1.examples) - Commit message: "docs(actor): add actor yaml examples"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Docs [Aditya]: Add `docs/reference/actors_examples.md` with strategist, executor, reviewer, tool-only, validation-node, and graph YAML examples. + - [ ] Tests (Behave) [Rui]: Add `features/actor_examples.feature` to ensure all examples validate. + - [ ] Tests (Robot) [Rui]: Add `robot/actor_examples.robot` to load each example. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_examples_load_bench.py` for YAML load throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Aditya]: `git commit -m "docs(actor): add actor yaml examples"`. -- [ ] **Stage C3: Skill Execution Framework** (Day 5-6) **[Jeff + Aditya - Critical Path]** - - [ ] Code: Implement skill execution framework - - [ ] **C3.1** [Jeff] Define `Skill` protocol in `src/cleveragents/actor/skills/protocol.py`: - - [ ] **C3.1a** Create abstract base using `typing.Protocol`: - ```python - class Skill(Protocol): - @property - def name(self) -> str: ... - @property - def description(self) -> str: ... - @property - def parameters(self) -> dict[str, Any]: ... # JSON Schema - @property - def metadata(self) -> SkillMetadata: ... - - async def execute( - self, - input_data: dict[str, Any], - context: SkillContext - ) -> SkillResult: ... - ``` - - [ ] **C3.1b** Define `SkillResult` dataclass: - - [ ] Field `success: bool` - whether execution succeeded - - [ ] Field `result: Any` - return value if successful - - [ ] Field `error: str | None` - error message if failed - - [ ] Field `changes: list[Change]` - changes made to resources - - [ ] Field `duration_ms: int` - execution time - - [ ] **C3.1c** Add docstrings explaining contract for skill implementers - - [ ] **C3.2** [Jeff] Define `SkillMetadata` Pydantic model in `src/cleveragents/actor/skills/metadata.py`: - - [ ] **C3.2a** Core capability fields: - - [ ] Field `read_only: bool = False` - only performs read operations - - [ ] Field `writes: bool = False` - can modify resources - - [ ] Field `write_scope: list[str] = []` - glob patterns for writable paths - - [ ] Field `idempotent: bool = False` - repeated calls produce same result - - [ ] Field `checkpointable: bool = False` - supports checkpoint/rollback - - [ ] Field `side_effects: list[str] = []` - external side effects (e.g., "network", "subprocess") - - [ ] **C3.2b** Safety and control fields: - - [ ] Field `human_approval_required: bool = False` - requires user confirmation - - [ ] Field `rate_limit: RateLimit | None = None` - calls per minute/hour - - [ ] Field `cost_profile: CostProfile | None = None` - estimated cost per call - - [ ] Field `timeout_seconds: int = 30` - maximum execution time - - [ ] **C3.2c** Define `RateLimit` and `CostProfile` models - - [ ] **C3.2d** Add validation to ensure `writes=True` if `write_scope` is non-empty - - [ ] **C3.3** [Jeff] Create `SkillContext` in `src/cleveragents/actor/skills/context.py`: - - [ ] **C3.3a** Define context fields: - - [ ] Field `plan_id: str` - current plan ULID - - [ ] Field `plan: Plan` - full plan object for reference - - [ ] Field `project: Project` - target project - - [ ] Field `resources: list[Resource]` - available resources - - [ ] Field `sandbox_manager: SandboxManager` - for sandbox access - - [ ] Field `changeset: ChangeSet` - accumulating changes - - [ ] Field `invocation_tracker: SkillInvocationTracker` - tracking calls - - [ ] Field `logger: logging.Logger` - skill-specific logger - - [ ] **C3.3b** Implement convenience methods: - - [ ] Method `get_file(path: str, resource: str | None = None) -> str`: - - [ ] Resolve path to sandboxed location - - [ ] Read and return file contents - - [ ] Raise FileNotFoundError if not exists - - [ ] Method `write_file(path: str, content: str, resource: str | None = None) -> Change`: - - [ ] Resolve path to sandboxed location - - [ ] Validate path against deny-list - - [ ] Create parent directories if needed - - [ ] Write content - - [ ] Create and record Change - - [ ] Return Change for tracking - - [ ] Method `edit_file(path: str, edits: list[Edit], resource: str | None = None) -> Change`: - - [ ] Read current content - - [ ] Apply edits sequentially - - [ ] Write modified content - - [ ] Record Change with edits - - [ ] Method `delete_file(path: str, resource: str | None = None) -> Change`: - - [ ] Validate file exists - - [ ] Delete file - - [ ] Record Change - - [ ] Method `list_files(pattern: str, resource: str | None = None) -> list[str]`: - - [ ] Resolve pattern to sandbox - - [ ] Return matching paths - - [ ] Method `search_files(pattern: str, content_pattern: str, resource: str | None = None) -> list[SearchResult]`: - - [ ] Search file contents with regex - - [ ] Return matches with file, line, context - - [ ] **C3.3c** Implement subplan spawning: - - [ ] Method `spawn_subplan(action: str, target_resources: list[str] | None = None, arguments: dict | None = None) -> str`: - - [ ] Validate action exists - - [ ] Create child plan with parent_plan_id = self.plan_id - - [ ] Queue subplan for execution - - [ ] Return subplan_id for tracking - - [ ] Record as `subplan_spawn` decision type - - [ ] **C3.3d** Implement read-only check enforcement: - - [ ] If plan.action.read_only is True, block all write operations - - [ ] Raise `ReadOnlyViolationError` if write attempted - - [ ] **C3.4** [Aditya] Implement `InlineSkillExecutor` in `src/cleveragents/actor/skills/inline_executor.py`: - - [ ] **C3.4a** Create class for executing inline Python code from actor YAML: - ```python - class InlineSkillExecutor: - def __init__(self, code: str, timeout: int = 30): - self.code = code - self.timeout = timeout - ``` - - [ ] **C3.4b** Create sandboxed execution environment: - - [ ] Restricted `__builtins__`: - - [ ] ALLOWED: `len`, `range`, `str`, `int`, `float`, `list`, `dict`, `set`, `tuple`, `bool`, `None`, `True`, `False`, `print`, `isinstance`, `hasattr`, `getattr`, `enumerate`, `zip`, `map`, `filter`, `sorted`, `reversed`, `any`, `all`, `min`, `max`, `sum`, `abs`, `round` - - [ ] BLOCKED: `open`, `exec`, `eval`, `compile`, `__import__`, `globals`, `locals`, `vars`, `dir`, `input` - - [ ] Inject `context: SkillContext` variable - - [ ] Inject `input_data: dict` variable - - [ ] Inject standard library modules: `re`, `json`, `datetime`, `collections`, `itertools`, `functools` - - [ ] **C3.4c** Execute code and capture result: - - [ ] Use `exec()` with restricted globals/locals - - [ ] Capture `result` variable as return value - - [ ] If no `result` variable, return None - - [ ] Wrap in asyncio.wait_for for timeout - - [ ] **C3.4d** Handle errors gracefully: - - [ ] Catch all exceptions during execution - - [ ] Convert to SkillResult with error message - - [ ] Include stack trace in error for debugging - - [ ] Log error with skill name and input - - [ ] **C3.4e** Add timeout support: - - [ ] Default 30 seconds - - [ ] Configurable via skill metadata - - [ ] Raise TimeoutError if exceeded - - [ ] **C3.5** [Aditya] Implement subplan spawning in skill context: - - [ ] **C3.5a** Update SkillContext.spawn_subplan to create real subplans: - - [ ] Call PlanLifecycleService.use_action() with parent_plan_id - - [ ] Set subplan's root_plan_id to parent's root_plan_id (or parent's id if root) - - [ ] Set subplan's automation_level from parent - - [ ] Return subplan_id - - [ ] **C3.5b** Add subplan tracking to parent plan: - - [ ] Store spawned subplan IDs in plan's execution_log - - [ ] Support querying all subplans of a plan - - [ ] **C3.5c** Add subplan completion handling: - - [ ] Parent plan can check subplan status - - [ ] Parent plan can collect subplan results - - [ ] Support waiting for subplan completion - - [ ] **C3.6** [Jeff + Luis] Implement built-in resource skills in `src/cleveragents/actor/skills/builtin/`: - - [ ] **C3.6a** [Jeff] Create base skill class in `__init__.py`: - ```python - class BuiltinSkill(ABC): - @abstractmethod - async def execute(self, input_data: dict, context: SkillContext) -> SkillResult: ... - - def _validate_path(self, path: str, context: SkillContext) -> str: - """Resolve and validate path against sandbox.""" - ... - ``` - - [ ] **C3.6b** [Jeff] File operation skills in `file_ops.py`: - - [ ] **ReadFileSkill**: - - [ ] Parameters: `path: str` (required) - - [ ] Metadata: `read_only=True` - - [ ] Implementation: resolve path, read via sandbox, return content - - [ ] Error handling: FileNotFoundError, PermissionError - - [ ] **WriteFileSkill**: - - [ ] Parameters: `path: str`, `content: str` - - [ ] Metadata: `writes=True, write_scope=['**/*']` - - [ ] Implementation: - - [ ] Validate path not in deny-list - - [ ] Create parent directories if needed - - [ ] Determine if create or modify operation - - [ ] Write content to sandbox - - [ ] Create Change record with operation type - - [ ] Record change in context.changeset - - [ ] Return: Change object with path and operation - - [ ] **EditFileSkill** (most complex - critical for coding): - - [ ] Parameters: `path: str`, `edits: list[Edit]` - - [ ] Metadata: `writes=True, idempotent=False` - - [ ] Implementation: - - [ ] Read original file content - - [ ] For each Edit in edits: - - [ ] If type=SEARCH_REPLACE: find `search` text, replace with `replace` - - [ ] If type=LINE_RANGE: replace lines start_line:end_line with content - - [ ] If type=INSERT_AFTER: insert content after matching line - - [ ] If type=INSERT_BEFORE: insert content before matching line - - [ ] If type=DELETE_LINES: remove lines start_line:end_line - - [ ] Track all changes made - - [ ] Write modified content - - [ ] Create Change with edits list - - [ ] Error handling: SearchTextNotFoundError, InvalidLineRangeError - - [ ] **DeleteFileSkill**: - - [ ] Parameters: `path: str` - - [ ] Metadata: `writes=True` - - [ ] Implementation: delete file, create DELETE Change - - [ ] **MoveFileSkill**: - - [ ] Parameters: `source: str`, `destination: str` - - [ ] Metadata: `writes=True` - - [ ] Implementation: move file, create MOVE Change with new_path - - [ ] **CopyFileSkill**: - - [ ] Parameters: `source: str`, `destination: str` - - [ ] Metadata: `writes=True` - - [ ] Implementation: copy file, create CREATE Change - - [ ] **C3.6c** [Luis] Directory operation skills in `dir_ops.py`: - - [ ] **CreateDirectorySkill**: - - [ ] Parameters: `path: str` - - [ ] Metadata: `writes=True` - - [ ] Implementation: create directory (and parents), record Change - - [ ] **ListDirectorySkill**: - - [ ] Parameters: `path: str`, `pattern: str = "*"`, `recursive: bool = False` - - [ ] Metadata: `read_only=True` - - [ ] Implementation: list matching files/dirs, return paths - - [ ] **DeleteDirectorySkill**: - - [ ] Parameters: `path: str`, `recursive: bool = False` - - [ ] Metadata: `writes=True` - - [ ] Implementation: delete directory, record DELETE Changes for all contents - - [ ] **C3.6d** [Luis] Search skills in `search_ops.py`: - - [ ] **SearchFilesSkill**: - - [ ] Parameters: `pattern: str` (glob), `content_pattern: str` (regex), `max_results: int = 100` - - [ ] Metadata: `read_only=True` - - [ ] Implementation: - - [ ] Find files matching glob pattern - - [ ] Search each file for content_pattern - - [ ] Return list of SearchResult(file, line_number, line_content, context) - - [ ] **FindDefinitionSkill** (uses tree-sitter for AST): - - [ ] Parameters: `symbol: str`, `language: str | None = None` - - [ ] Metadata: `read_only=True` - - [ ] Implementation: - - [ ] Parse files with tree-sitter - - [ ] Find function/class/variable definitions - - [ ] Return list of Location(file, line, column, snippet) - - [ ] **FindReferencesSkill**: - - [ ] Parameters: `symbol: str` - - [ ] Metadata: `read_only=True` - - [ ] Implementation: find all usages of symbol - - [ ] **GetFileInfoSkill**: - - [ ] Parameters: `path: str` - - [ ] Metadata: `read_only=True` - - [ ] Implementation: return FileInfo(size, mtime, language, line_count) - - [ ] **C3.6e** [Hamza] Git operation skills in `git_ops.py`: - - [ ] **GitStatusSkill**: - - [ ] Parameters: (none) - - [ ] Metadata: `read_only=True` - - [ ] Implementation: run `git status --porcelain`, parse output - - [ ] **GitDiffSkill**: - - [ ] Parameters: `path: str | None = None`, `staged: bool = False` - - [ ] Metadata: `read_only=True` - - [ ] Implementation: run `git diff [--staged] [path]` - - [ ] **GitLogSkill**: - - [ ] Parameters: `count: int = 10`, `path: str | None = None` - - [ ] Metadata: `read_only=True` - - [ ] Implementation: run `git log --oneline -n {count}` - - [ ] **GitBlameSkill**: - - [ ] Parameters: `path: str`, `start_line: int | None`, `end_line: int | None` - - [ ] Metadata: `read_only=True` - - [ ] Implementation: run `git blame -L {start},{end} {path}` - - [ ] **C3.6f** [Jeff] All built-in skills must implement: - - [ ] Full SkillMetadata with accurate capability flags - - [ ] Proper error handling with descriptive messages - - [ ] Logging of all operations for debugging - - [ ] Path validation against sandbox and deny-lists - - [ ] Change recording for all write operations - - [ ] Async execution support - - [ ] **C3.6g** [Jeff] Create skill registration in `src/cleveragents/actor/skills/registry.py`: - - [ ] `BuiltinSkillRegistry` singleton with all built-in skills - - [ ] Method `get_skill(name: str) -> Skill | None` - - [ ] Method `list_skills() -> list[Skill]` - - [ ] Method `list_by_capability(read_only: bool = None, writes: bool = None) -> list[Skill]` - - [ ] Register all C3.6 skills on import - - [ ] **C3.7** [Aditya] Implement MCP skill adapter in `src/cleveragents/actor/skills/mcp_adapter.py`: - - [ ] **C3.7a** `MCPServerConnection` class: - - [ ] Connect to MCP server via stdio or SSE transport - - [ ] List available tools from server - - [ ] Call tools with JSON-RPC - - [ ] Handle server lifecycle (start/stop) - - [ ] **C3.7b** `MCPSkillAdapter` class: - - [ ] Wrap MCP tool as CleverAgents Skill - - [ ] Infer SkillMetadata from MCP tool schema - - [ ] Intercept calls for sandbox path rewriting - - [ ] Record changes when MCP tool modifies resources - - [ ] **C3.7c** Actor YAML integration: - - [ ] Parse `mcp_servers` config in actor definition - - [ ] Auto-register MCP tools as skills in actor context - - [ ] Environment variable substitution for secrets - - [ ] Tests: Integration tests for skill execution - - [ ] **C3.8** [Rui] Write Behave scenarios in `features/skill_execution.feature`: - - [ ] Scenario: Execute inline Python skill with context - - [ ] Scenario: Skill can read files from sandbox - - [ ] Scenario: Skill can write files to sandbox - - [ ] Scenario: Skill with invalid code produces error - - [ ] Scenario: Skill timeout prevents infinite loops - - [ ] Scenario: spawn_subplan creates child plan - - [ ] **C3.9** [Rui] Write Behave scenarios in `features/builtin_skills.feature`: - - [ ] Scenario: WriteFileSkill creates file and records Change - - [ ] Scenario: EditFileSkill applies search/replace edit - - [ ] Scenario: DeleteFileSkill removes file and records Change - - [ ] Scenario: MoveFileSkill renames file and records Change - - [ ] Scenario: ListDirectorySkill returns matching files - - [ ] Scenario: SearchFilesSkill finds content matches - - [ ] Scenario: Skill respects deny-list patterns (.git/, node_modules/) - - [ ] Scenario: Skill enforces sandbox boundaries - - [ ] **C3.10** [Rui] Write Behave scenarios in `features/mcp_integration.feature`: - - [ ] Scenario: Connect to MCP server and list tools - - [ ] Scenario: MCP tool becomes available as skill - - [ ] Scenario: MCP tool call is intercepted for sandbox paths - - [ ] Scenario: MCP tool writes are recorded in ChangeSet +**Parallel Group C2: Actor Loading & Compilation [Aditya + Jeff]** (depends on C1) +- [ ] **COMMIT (Owner: Aditya | Group: C2.loader) - Commit message: "feat(actor): add actor registry and loader"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Aditya]: Implement actor loader/registry with namespaced lookup and cache invalidation. + - [ ] Code [Aditya]: Add registry integration with Tool Registry so tool nodes resolve at load time. + - [ ] Docs [Aditya]: Add `docs/reference/actors_loading.md` with discovery rules and namespaces. + - [ ] Tests (Behave) [Rui]: Add `features/actor_loading.feature` for discovery, duplicates, and namespace lookup. + - [ ] Tests (Robot) [Rui]: Add `robot/actor_loading.robot` for loader smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_loading_bench.py` for registry load performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Aditya]: `git commit -m "feat(actor): add actor registry and loader"`. +- [ ] **COMMIT (Owner: Jeff | Group: C2.compiler) - Commit message: "feat(actor): compile actor configs to LangGraph"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Implement ActorCompiler that builds LangGraph for LLM, TOOL, and GRAPH actors with tool node wiring. + - [ ] Code [Jeff]: Resolve tool node references through Tool Registry and validate required bindings before compile. + - [ ] Docs [Jeff]: Add `docs/reference/actors_compilation.md` covering compile outputs and error modes. + - [ ] Tests (Behave) [Rui]: Add `features/actor_compilation.feature` for LLM/GRAPH compilation and tool node wiring. + - [ ] Tests (Robot) [Rui]: Add `robot/actor_compilation.robot` smoke test compiling all examples. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_compilation_bench.py` for compilation speed. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(actor): compile actor configs to LangGraph"`. +- [ ] **COMMIT (Owner: Jeff | Group: C2.refs) - Commit message: "feat(actor): resolve actor references and subgraphs"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Implement reference resolution, cycle detection, and subgraph wiring for actor refs. + - [ ] Code [Jeff]: Ensure cross-namespace reference resolution follows `[server:]namespace/name` rules. + - [ ] Docs [Jeff]: Update `docs/reference/actors_compilation.md` with reference semantics. + - [ ] Tests (Behave) [Rui]: Add `features/actor_reference_resolution.feature` for missing/recursive refs. + - [ ] Tests (Robot) [Rui]: Add `robot/actor_reference_resolution.robot` for subgraph wiring. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_reference_bench.py` for reference resolution performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(actor): resolve actor references and subgraphs"`. -- [ ] **Stage C4: Tool-Based Change Tracking** (Day 8-10) **[Luis - Architectural]** - - [ ] Code: Implement tool-based change tracking (NOT output parsing) - - [ ] **CRITICAL ARCHITECTURE**: ChangeSet is built from skill/tool invocations, NOT by parsing LLM text output - - [ ] **C4.1** [Luis] Update `src/cleveragents/domain/models/core/change.py`: - - [ ] Enhance `Change` model with: - - [ ] Field `operation: OperationType` - create/modify/delete/move - - [ ] Field `path: str` - target resource path - - [ ] Field `new_path: str | None` - for move operations - - [ ] Field `content: str | None` - full content (for create) - - [ ] Field `edits: list[Edit] | None` - targeted edits (for modify) - - [ ] Field `patch: str | None` - unified diff (generated, not parsed) - - [ ] Field `language: str | None` - detected language - - [ ] Field `skill_invocation_id: str` - which skill call produced this - - [ ] Field `timestamp: datetime` - when change was made - - [ ] Field `validation_result: ValidationResult | None` - - [ ] Define `Edit` model for targeted edits: - - [ ] Field `type: EditType` - search_replace, line_range, insert_after, etc. - - [ ] Field `search: str | None` - text to find (for search_replace) - - [ ] Field `replace: str | None` - replacement text - - [ ] Field `start_line: int | None` - for line-based edits - - [ ] Field `end_line: int | None` - for line-based edits - - [ ] Field `content: str | None` - content to insert - - [ ] Enhance `ChangeSet` model with: - - [ ] Field `changes: list[Change]` - all resource changes - - [ ] Field `warnings: list[str]` - non-blocking issues - - [ ] Field `skill_invocations: list[SkillInvocation]` - full invocation history - - [ ] Field `generated_by_actor: str` - actor that produced this - - [ ] Field `validation: ChangeSetValidation` - overall validation - - [ ] Method `add_change(change: Change)` - append change from skill - - [ ] Method `get_change(path: str) -> Change | None` - find by path - - [ ] Method `get_changes_by_skill(skill_id: str) -> list[Change]` - changes from specific skill - - [ ] Method `file_paths() -> list[str]` - all affected paths - - [ ] Method `to_diff() -> str` - unified diff of all changes - - [ ] Method `rollback_to(change_id: str)` - remove changes after point - - [ ] **C4.2** [Luis] Implement `SkillInvocationTracker` in `src/cleveragents/actor/skills/tracker.py`: - - [ ] **C4.2a** `SkillInvocation` model: - - [ ] Field `id: str` - unique invocation ID - - [ ] Field `skill_name: str` - which skill was called - - [ ] Field `parameters: dict` - input parameters - - [ ] Field `result: Any` - skill return value - - [ ] Field `changes: list[Change]` - resource changes produced - - [ ] Field `timestamp: datetime` - when invoked - - [ ] Field `duration_ms: int` - execution time - - [ ] Field `error: str | None` - if skill failed - - [ ] **C4.2b** `SkillInvocationTracker` class: - - [ ] Method `start_invocation(skill: Skill, params: dict) -> str` - begin tracking - - [ ] Method `record_change(invocation_id: str, change: Change)` - record change - - [ ] Method `complete_invocation(invocation_id: str, result: Any)` - finish tracking - - [ ] Method `fail_invocation(invocation_id: str, error: Exception)` - record failure - - [ ] Method `get_invocations() -> list[SkillInvocation]` - full history - - [ ] Method `build_changeset() -> ChangeSet` - assemble from invocations - - [ ] **C4.3** [Luis] Implement `ToolCallRouter` in `src/cleveragents/actor/skills/router.py`: - - [ ] **C4.3a** Parse LLM tool calls (NOT text output): - - [ ] Handle OpenAI-style tool_calls from response - - [ ] Handle Anthropic-style tool_use blocks - - [ ] Handle LangChain AgentAction format - - [ ] **C4.3b** Route tool calls to skills: - - [ ] Look up skill by name in registry - - [ ] Validate parameters against skill schema - - [ ] Check capability metadata (read_only, writes, etc.) - - [ ] Enforce permission restrictions - - [ ] **C4.3c** Execute with tracking: - - [ ] Start invocation tracking - - [ ] Execute skill in sandbox context - - [ ] Record changes produced by skill - - [ ] Complete invocation tracking - - [ ] Return result to LLM - - [ ] **C4.4** [Luis] Implement resource path validation in `src/cleveragents/actor/skills/path_validator.py`: - - [ ] Validate paths against sandbox boundaries - - [ ] Enforce deny-list patterns (.git/, node_modules/, __pycache__/, etc.) - - [ ] Auto-create parent directories for new file paths - - [ ] Resolve relative paths to absolute sandbox paths - - [ ] Detect path traversal attempts (../) - - [ ] **C4.5** [Luis] Create diff generation in `src/cleveragents/agents/diff_generator.py`: - - [ ] Method `generate_unified_diff(change: Change, sandbox: Sandbox) -> str`: - - [ ] Compare sandbox state with original - - [ ] Generate unified diff format - - [ ] Include file headers with paths - - [ ] Method `generate_changeset_diff(changeset: ChangeSet, sandbox: Sandbox) -> str`: - - [ ] Combine all change diffs - - [ ] Add summary header (files created, modified, deleted) - - [ ] Include statistics (lines added/removed) - - [ ] Method `generate_edit_preview(edit: Edit, original: str) -> str`: - - [ ] Show what an edit will change - - [ ] Highlight search/replace matches - - [ ] Tests: Tool-based change tracking tests - - [ ] **C4.6** [Rui] Write Behave scenarios in `features/change_tracking.feature`: - - [ ] Scenario: Skill invocation creates Change record - - [ ] Scenario: Multiple skill calls accumulate in ChangeSet - - [ ] Scenario: ChangeSet correctly tracks skill invocation history - - [ ] Scenario: Failed skill invocation is recorded with error - - [ ] Scenario: Generate unified diff from ChangeSet - - [ ] Scenario: Rollback to specific change point - - [ ] **C4.7** [Rui] Write Behave scenarios in `features/tool_call_routing.feature`: - - [ ] Scenario: Route OpenAI-style tool call to skill - - [ ] Scenario: Route Anthropic-style tool_use to skill - - [ ] Scenario: Validate parameters against skill schema - - [ ] Scenario: Reject tool call for read_only skill trying to write - - [ ] Scenario: Path validation rejects traversal attempt - - [ ] Scenario: Path validation auto-creates directories +**Parallel Group C3: Skill Protocol & Context [Jeff]** (critical path; depends on C1) +- [ ] **COMMIT (Owner: Jeff | Group: C3.protocol) - Commit message: "feat(skill): add skill protocol and metadata"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Define Skill protocol interface, SkillMetadata, SkillResult, and SkillError types. + - [ ] Code [Jeff]: Add `SkillDefinition` model that references Tool Registry names and optional inline tool definitions. + - [ ] Docs [Jeff]: Add `docs/reference/skills_protocol.md` describing metadata, tool composition, and JSON schema rules. + - [ ] Tests (Behave) [Rui]: Add `features/skill_protocol.feature` for metadata validation and error capture. + - [ ] Tests (Robot) [Rui]: Add `robot/skill_protocol.robot` smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/skill_protocol_bench.py` for validation throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(skill): add skill protocol and metadata"`. +- [ ] **COMMIT (Owner: Jeff | Group: C3.context) - Commit message: "feat(skill): add skill context and registry"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Implement SkillContext (plan/resource access, sandbox path, change tracker) and SkillRegistry. + - [ ] Code [Jeff]: Wire SkillRegistry to Tool Registry for tool resolution and validation node inclusion. + - [ ] Docs [Jeff]: Add `docs/reference/skills_context.md` with context fields and helper methods. + - [ ] Tests (Behave) [Rui]: Add `features/skill_context.feature` for sandboxed access and registry resolution. + - [ ] Tests (Robot) [Rui]: Add `robot/skill_context.robot` for registry smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/skill_context_bench.py` for registry resolution overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(skill): add skill context and registry"`. +- [ ] **COMMIT (Owner: Jeff | Group: C3.inline) - Commit message: "feat(skill): add inline tool executor"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Implement inline tool execution with timeouts and restricted environment. + - [ ] Code [Jeff]: Ensure inline tools conform to Tool Registry schema and return structured results. + - [ ] Docs [Jeff]: Add `docs/reference/skills_inline.md` with safety constraints. + - [ ] Tests (Behave) [Rui]: Add `features/skill_inline.feature` for execution and timeout handling. + - [ ] Tests (Robot) [Rui]: Add `robot/skill_inline.robot` for inline tool smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/inline_tool_bench.py` for execution overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(skill): add inline tool executor"`. -- [ ] **Stage C5: Validation Pipeline** (Day 9-11) **[Luis]** - - [ ] Code: Implement real validation gates - - [ ] **C5.1** [Luis] Create `ValidationPipeline` in `src/cleveragents/application/services/validation_service.py`: - - [ ] Method `validate_changeset(changeset: ChangeSet, project: Project) -> ValidationResult`: - - [ ] Run all applicable validators - - [ ] Aggregate results - - [ ] Return pass/fail with details - - [ ] Method `validate_syntax(change: Change) -> ValidationResult`: - - [ ] Detect language from extension - - [ ] Run language-specific syntax check - - [ ] Method `validate_lint(change: Change, config: ValidationConfig) -> ValidationResult`: - - [ ] Run lint command from project config - - [ ] Parse lint output for errors - - [ ] Method `validate_tests(project: Project, config: ValidationConfig) -> ValidationResult`: - - [ ] Run test command from project config - - [ ] Parse test results - - [ ] Method `validate_build(project: Project, config: ValidationConfig) -> ValidationResult`: - - [ ] Run build command from project config - - [ ] Check for build errors - - [ ] **C5.2** [Luis] Implement language-specific validators: - - [ ] Python: `python -m py_compile ` - - [ ] JavaScript/TypeScript: `node --check ` or syntax parse - - [ ] JSON: `json.loads()` validation - - [ ] YAML: `yaml.safe_load()` validation - - [ ] **C5.3** [Luis] Implement validation failure handling: - - [ ] If validation fails, attempt repair loop: - - [ ] Send errors to LLM with request to fix - - [ ] Parse fixed output - - [ ] Re-validate - - [ ] Max 3 repair attempts - - [ ] If repair fails, mark changeset as errored - - [ ] Preserve original output for debugging - - [ ] **C5.4** [Luis] Remove stub validation from existing code: - - [ ] Replace "output length > 10" check with real validation - - [ ] Remove "PASS" stub validation - - [ ] Tests: Validation tests - - [ ] **C5.5** [Rui] Write Behave scenarios in `features/validation_pipeline.feature`: - - [ ] Scenario: Valid Python file passes syntax validation - - [ ] Scenario: Invalid Python file fails with clear error - - [ ] Scenario: Lint errors detected and reported - - [ ] Scenario: Test failures detected and reported - - [ ] Scenario: Validation repair loop fixes simple errors - - [ ] Scenario: Validation repair gives up after max attempts +**Parallel Group C4: Built-in Skills [Jeff + Luis]** (depends on C3) +- [ ] **COMMIT (Owner: Jeff | Group: C4.file) - Commit message: "feat(skill): add file operation skills"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Implement ReadFile, WriteFile, EditFile, and DeleteFile tools with read_only enforcement. + - [ ] Code [Jeff]: Register tools in Tool Registry with resource bindings for fs/git resources. + - [ ] Docs [Jeff]: Add `docs/reference/skills_file.md` with examples and error cases. + - [ ] Tests (Behave) [Rui]: Add `features/skill_file_ops.feature` for read/write/edit/delete flows. + - [ ] Tests (Robot) [Rui]: Add `robot/skill_file_ops.robot` for file ops integration. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/file_tool_bench.py` for read/write throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(skill): add file operation skills"`. +- [ ] **COMMIT (Owner: Jeff | Group: C4.search) - Commit message: "feat(skill): add directory and search skills"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Implement ListDir, Glob, and Grep tools with ignore patterns and size limits. + - [ ] Code [Jeff]: Register tools in Tool Registry with resource bindings and sandbox awareness. + - [ ] Docs [Jeff]: Add `docs/reference/skills_search.md` with examples. + - [ ] Tests (Behave) [Rui]: Add `features/skill_search.feature` for listing/globbing/searching. + - [ ] Tests (Robot) [Rui]: Add `robot/skill_search.robot` for search integration. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/search_tool_bench.py` for search performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(skill): add directory and search skills"`. +- [ ] **COMMIT (Owner: Luis | Group: C4.git) - Commit message: "feat(skill): add git operation skills"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Implement read-only git tools (status, diff, log, show) for sandboxed repos. + - [ ] Code [Luis]: Register git tools in Tool Registry with read-only capability metadata. + - [ ] Docs [Luis]: Add `docs/reference/skills_git.md` clarifying no destructive ops in MVP. + - [ ] Tests (Behave) [Rui]: Add `features/skill_git.feature` for git tool outputs. + - [ ] Tests (Robot) [Rui]: Add `robot/skill_git.robot` for git tool integration. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/git_tool_bench.py` for diff/log performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(skill): add git operation skills"`. -- [ ] **Stage C6: Built-in Provider Actors** (Day 10-11) **[Aditya]** - - [ ] Code: Create built-in actors for each provider - - [ ] **C6.1** [Aditya] Generate built-in actor configs in `src/cleveragents/actor/builtins.py`: - - [ ] `openai/gpt-4` - GPT-4 wrapper - - [ ] `openai/gpt-4-turbo` - GPT-4 Turbo wrapper - - [ ] `openai/gpt-3.5-turbo` - GPT-3.5 wrapper - - [ ] `anthropic/claude-3-opus` - Claude 3 Opus wrapper - - [ ] `anthropic/claude-3-sonnet` - Claude 3 Sonnet wrapper - - [ ] `anthropic/claude-3-haiku` - Claude 3 Haiku wrapper - - [ ] `google/gemini-pro` - Gemini Pro wrapper - - [ ] `google/gemini-ultra` - Gemini Ultra wrapper - - [ ] **C6.2** [Aditya] Ensure built-in actors work as strategy/execution actors: - - [ ] Add appropriate system prompts for each role - - [ ] Configure temperature defaults (lower for execution) - - [ ] Test with plan lifecycle - - [ ] **C6.3** [Aditya] Implement provider capability detection: - - [ ] Check which API keys are configured - - [ ] Only register actors for available providers - - [ ] Clear error message for unavailable providers - - [ ] Tests: Verify built-in actors work in plan lifecycle - - [ ] **C6.4** [Rui] Write Behave scenarios in `features/builtin_actors.feature`: - - [ ] Scenario: Built-in OpenAI actor loads correctly - - [ ] Scenario: Built-in Anthropic actor loads correctly - - [ ] Scenario: Built-in actor can be used as strategy actor - - [ ] Scenario: Missing API key produces clear error +**Parallel Group C5: Tool Routing & Change Tracking [Luis + Jeff]** (depends on C3/C4) +- [ ] **COMMIT (Owner: Luis | Group: C5.model) - Commit message: "feat(change): add ChangeSet models and invocation tracker"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Add Change/ChangeSet/ToolInvocation models and SkillInvocationTracker. + - [ ] Code [Luis]: Ensure ChangeSet stores resource references, sandbox paths, and tool metadata. + - [ ] Docs [Luis]: Add `docs/reference/change_tracking.md` describing tool-to-change mapping. + - [ ] Tests (Behave) [Rui]: Add `features/change_tracking.feature` for ChangeSet aggregation. + - [ ] Tests (Robot) [Rui]: Add `robot/change_tracking.robot` for tracker smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/change_tracking_bench.py` for invocation tracking overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(change): add ChangeSet models and invocation tracker"`. +- [ ] **COMMIT (Owner: Jeff | Group: C5.router) - Commit message: "feat(change): add tool router for providers"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Implement ToolCallRouter for OpenAI/Anthropic/LangChain tool schemas with deterministic IDs. + - [ ] Code [Jeff]: Add mapping for tool/validation names and argument schemas based on Tool Registry metadata. + - [ ] Docs [Jeff]: Add `docs/reference/tool_router.md` with provider-specific mappings. + - [ ] Tests (Behave) [Rui]: Add `features/tool_router.feature` for schema mapping. + - [ ] Tests (Robot) [Rui]: Add `robot/tool_router.robot` for routing smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_router_bench.py` for routing performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(change): add tool router for providers"`. +- [ ] **COMMIT (Owner: Luis | Group: C5.diff) - Commit message: "feat(change): add diff review artifacts"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Implement DiffBuilder and ReviewArtifact models for CLI review. + - [ ] Code [Luis]: Add support for multi-resource diffs and per-resource grouping. + - [ ] Docs [Luis]: Add `docs/reference/diff_review.md` with output format. + - [ ] Tests (Behave) [Rui]: Add `features/diff_review.feature` for diff generation. + - [ ] Tests (Robot) [Rui]: Add `robot/diff_review.robot` for review artifacts. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/diff_review_bench.py` for diff building performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(change): add diff review artifacts"`. -- [ ] **Stage C7: Plan-Actor Integration** (Day 11-14) **[Aditya + Luis]** - - [ ] Code: Connect actors to plan lifecycle - - [ ] **C7.1** [Aditya] Update `PlanLifecycleService.execute_strategize()`: - - [ ] Load strategy_actor from action - - [ ] Compile actor to LangGraph - - [ ] Build strategy context: - - [ ] Project resources - - [ ] Plan description and arguments - - [ ] Definition of done - - [ ] Invoke actor graph with context - - [ ] Parse strategy output - - [ ] Store strategy in plan - - [ ] **C7.2** [Aditya] Implement dependency closure computation: - - [ ] Method `compute_closure(target: str, project: Project) -> ResourceClosure`: - - [ ] Find direct imports/includes - - [ ] Find symbol dependencies - - [ ] Find test dependencies - - [ ] Find build references - - [ ] Integrate with strategy actor context - - [ ] **C7.3** [Luis] Update `PlanLifecycleService.execute_execution()`: - - [ ] Load execution_actor from action - - [ ] Compile actor to LangGraph - - [ ] Build execution context: - - [ ] Strategy output - - [ ] Resource service for sandbox access - - [ ] Bounded dependency closure - - [ ] Invoke actor graph with context - - [ ] Parse output as ChangeSet - - [ ] Run validation pipeline - - [ ] Handle subplan spawning - - [ ] **C7.4** [Luis] Update `PlanLifecycleService.apply_plan()`: - - [ ] Verify execution completed successfully - - [ ] Commit all sandboxes - - [ ] Apply ChangeSet to resources - - [ ] Record applied artifacts - - [ ] Clean up sandboxes - - [ ] **C7.4a** [Luis] Implement ATOMIC apply: - - [ ] Apply must be all-or-nothing: - - [ ] Write all changes to temp files first - - [ ] Validate all writes succeeded - - [ ] Atomic rename/swap to final locations - - [ ] If any step fails, rollback all changes - - [ ] In git mode: - - [ ] All changes in single commit - - [ ] If commit fails, no partial changes applied - - [ ] Handle partial failure: - - [ ] Preserve sandbox for inspection - - [ ] Clear error message about what failed - - [ ] Allow retry after manual fix - - [ ] **C7.5** [Aditya] Implement hierarchical task decomposition: - - [ ] Strategy actor can emit subplan decisions - - [ ] Each subplan gets bounded context - - [ ] Parallel or sequential execution modes - - [ ] **C7.6** [Luis] Connect output parser to execution flow: - - [ ] After actor produces output, parse to ChangeSet - - [ ] Validate ChangeSet - - [ ] Store ChangeSet in plan - - [ ] **C7.7** [Luis] Implement diff review artifact storage: - - [ ] Store generated diff in plan metadata - - [ ] Create `DiffArtifact` model: - - [ ] Field `diff_id: str` - ULID - - [ ] Field `plan_id: str` - parent plan - - [ ] Field `unified_diff: str` - full unified diff - - [ ] Field `file_summaries: list[FileSummary]` - per-file summary - - [ ] Field `risk_markers: list[str]` - touched auth code, migrations, etc. - - [ ] Field `created_at: datetime` - - [ ] Display diff in `agents [--data-dir PATH] [--config-path PATH] plan diff` command - - [ ] Display diff before apply in review-before-apply mode - - [ ] Tests: End-to-end tests for full plan lifecycle with actors - - [ ] **C7.7** [Rui] Write Behave scenarios in `features/plan_actor_integration.feature`: - - [ ] Scenario: Full lifecycle with LLM actors (mocked) - - [ ] Scenario: Strategy actor receives correct context - - [ ] Scenario: Execution actor receives strategy output - - [ ] Scenario: ChangeSet applied correctly - - [ ] Scenario: Validation failure triggers repair loop - - [ ] **C7.8** [Rui] Write Robot integration test `robot/plan_actor_integration.robot`: - - [ ] Test: Full lifecycle with real sandbox - - [ ] Test: Multi-file generation and application - - [ ] Tests: Dependency closure computation accuracy - - [ ] **C7.9** [Rui] Write Behave scenarios in `features/dependency_closure.feature`: - - [ ] Scenario: Python imports detected correctly - - [ ] Scenario: Test file dependencies included +**Parallel Group C6: Validation Pipeline [Luis + Jeff]** (depends on C5 and project validation config) +- [ ] **COMMIT (Owner: Luis | Group: C6.pipeline) - Commit message: "feat(validation): add validation pipeline and results model"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Implement ValidationCommand, ValidationResult, and ValidationPipeline using Validation attachments from Tool Registry. + - [ ] Code [Luis]: Run validations at end of Execute phase only; do not re-run during Apply per spec. + - [ ] Code [Luis]: Enforce required vs informational validation modes and fix-then-revalidate loop hooks. + - [ ] Docs [Luis]: Add `docs/reference/validation_pipeline.md` with ordering, timeouts, and failure handling. + - [ ] Tests (Behave) [Rui]: Add `features/validation_pipeline.feature` for pass/fail paths and required/informational modes. + - [ ] Tests (Robot) [Rui]: Add `robot/validation_pipeline.robot` for pipeline smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/validation_pipeline_bench.py` for pipeline runtime. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(validation): add validation pipeline and results model"`. +- [ ] **COMMIT (Owner: Jeff | Group: C6.gating) - Commit message: "feat(validation): integrate validation with apply gating"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Block apply on required validation failure; surface validation artifacts for review. + - [ ] Code [Jeff]: Ensure informational validation failures do not block apply but are logged in plan status. + - [ ] Docs [Jeff]: Update `docs/reference/plan_actor_integration.md` with validation gating behavior. + - [ ] Tests (Behave) [Rui]: Add `features/validation_gating.feature` for apply blocking. + - [ ] Tests (Robot) [Rui]: Add `robot/validation_gating.robot` for end-to-end gating. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/validation_gating_bench.py` for gating overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(validation): integrate validation with apply gating"`. + +**Parallel Group C7: MCP Adapter [Aditya]** (depends on C3) +- [ ] **COMMIT (Owner: Aditya | Group: C7.mcp) - Commit message: "feat(skill): add MCP adapter for external tools"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Aditya]: Implement MCP client adapter conforming to Tool interface with connection config. + - [ ] Code [Aditya]: Register MCP tools in Tool Registry with dynamic discovery from MCP server. + - [ ] Docs [Aditya]: Add `docs/reference/skills_mcp.md` with server connection examples. + - [ ] Tests (Behave) [Rui]: Add `features/skill_mcp.feature` for MCP tool calls. + - [ ] Tests (Robot) [Rui]: Add `robot/skill_mcp.robot` for MCP adapter smoke test. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/mcp_adapter_bench.py` for tool invocation latency. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Aditya]: `git commit -m "feat(skill): add MCP adapter for external tools"`. + +**Parallel Group C8: Built-in Provider Actors [Aditya]** (depends on C1/C2) +- [ ] **COMMIT (Owner: Aditya | Group: C8.providers) - Commit message: "feat(actor): add built-in provider actors"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Aditya]: Add built-in actor configs for `openai/`, `anthropic/`, and `openrouter/` (plus `google/` if configured). + - [ ] Code [Aditya]: Add built-in actors for invariant reconciliation and estimation roles (using provider defaults). + - [ ] Docs [Aditya]: Add `docs/reference/provider_actors.md` with provider defaults. + - [ ] Tests (Behave) [Rui]: Add `features/provider_actors.feature` for built-in actor loading. + - [ ] Tests (Robot) [Rui]: Add `robot/provider_actors.robot` for registry visibility. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/provider_actor_load_bench.py` for registry load cost. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Aditya]: `git commit -m "feat(actor): add built-in provider actors"`. + +**Parallel Group C9: Plan-Actor Integration [Jeff + Luis]** (depends on C2/C5/C6) +- [ ] **COMMIT (Owner: Jeff | Group: C9.execute) - Commit message: "feat(plan): execute strategize and execute phases via actors"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Connect PlanLifecycleService to actor execution for Strategize and Execute phases. + - [ ] Code [Jeff]: Ensure Strategize is read-only and records decisions without modifying resources. + - [ ] Code [Jeff]: Ensure Execute uses sandbox resources and tool calls routed through Tool Router + ChangeSet. + - [ ] Docs [Jeff]: Add `docs/reference/plan_actor_integration.md` with phase flow. + - [ ] Tests (Behave) [Rui]: Add `features/plan_actor_integration.feature` for strategy/execute flows. + - [ ] Tests (Robot) [Rui]: Add `robot/plan_actor_integration.robot` for end-to-end actor execution. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_actor_integration_bench.py` for execution overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(plan): execute strategize and execute phases via actors"`. +- [ ] **COMMIT (Owner: Jeff | Group: C9.apply) - Commit message: "feat(plan): integrate change review and apply flow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Wire ChangeSet review artifacts into `plan diff` and review-before-apply flow. + - [ ] Code [Jeff]: Ensure Apply merges sandbox into real resources only after required validations pass. + - [ ] Docs [Jeff]: Update CLI docs for `plan diff` and `plan apply` review output. + - [ ] Tests (Behave) [Rui]: Add `features/plan_review_apply.feature` for review gate behavior. + - [ ] Tests (Robot) [Rui]: Add `robot/plan_review_apply.robot` for review-before-apply path. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_apply_bench.py` for apply throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(plan): integrate change review and apply flow"`. **M3 SUCCESS CRITERIA**: - [ ] Can define actors in YAML with skills @@ -4833,1762 +3583,127 @@ TESTING [Rui - Continuous]: Write tests BEFORE implementation **Target: Milestone M4 (+21 days)** -- [ ] **Stage D1: Decision Data Model** (Day 15-16) **[Hamza - Well Rounded]** - - **SEQUENTIAL ORDER**: D1.1 (Enums) → D1.2 (ContextSnapshot) → D1.3 (Decision model) → D1.4 (Helpers) → D1.5 (Tests) - - - [ ] Code: Create Decision domain model - - [ ] **D1.1** [Hamza] Define `DecisionType` enum in `src/cleveragents/domain/models/core/decision.py`: - - [ ] **D1.1a** [Hamza] Create file with DecisionType enum: - ```python - from enum import Enum - - class DecisionType(str, Enum): - """Classification of decision points in plan execution.""" - - # Root decisions - PROMPT_DEFINITION = "prompt_definition" # Initial plan prompt - - # Strategy phase decisions - STRATEGY_CHOICE = "strategy_choice" # High-level approach - IMPLEMENTATION_CHOICE = "implementation_choice" # How to implement - RESOURCE_SELECTION = "resource_selection" # Which resources to use - - # Execution phase decisions - SUBPLAN_SPAWN = "subplan_spawn" # Decision to create subplan - TOOL_INVOCATION = "tool_invocation" # Which tool/skill to use - - # Error handling decisions - ERROR_RECOVERY = "error_recovery" # How to handle failure - VALIDATION_RESPONSE = "validation_response" # Response to validation failure - - # User interaction decisions - USER_INTERVENTION = "user_intervention" # User provided guidance - ``` - - [ ] Commit: "feat(domain): define DecisionType enum" - - [ ] **D1.1b** [Hamza] Add helper method for decision classification: - ```python - @classmethod - def is_strategy_decision(cls, decision_type: "DecisionType") -> bool: - """Check if this is a strategy phase decision.""" - return decision_type in { - cls.PROMPT_DEFINITION, cls.STRATEGY_CHOICE, - cls.IMPLEMENTATION_CHOICE, cls.RESOURCE_SELECTION - } - - @classmethod - def is_execution_decision(cls, decision_type: "DecisionType") -> bool: - """Check if this is an execution phase decision.""" - return decision_type in {cls.SUBPLAN_SPAWN, cls.TOOL_INVOCATION} - ``` - - [ ] Commit: "feat(domain): add DecisionType helper methods" - - [ ] **D1.2** [Hamza] Define `ContextSnapshot` model: - - [ ] **D1.2a** [Hamza] Create ContextSnapshot dataclass: - ```python - @dataclass(frozen=True) - class ContextSnapshot: - """Snapshot of context at decision point for replay.""" - - snapshot_id: str # ULID - hot_context_hash: str # SHA-256 hash of hot context content - hot_context_ref: str # Storage reference (file path or blob ID) - relevant_resources: tuple[str, ...] # Resource IDs in scope - actor_state_ref: str | None # LangGraph checkpoint ID - file_versions: dict[str, str] # path -> git commit or hash - created_at: datetime - ``` - - [ ] Commit: "feat(domain): define ContextSnapshot dataclass" - - [ ] **D1.2b** [Hamza] Add factory method: - ```python - @classmethod - def capture( - cls, - hot_context: str, - resources: list[str], - actor_state: str | None = None, - file_versions: dict[str, str] | None = None - ) -> "ContextSnapshot": - """Capture a snapshot of current context.""" - import hashlib - import ulid - - return cls( - snapshot_id=ulid.new().str, - hot_context_hash=hashlib.sha256(hot_context.encode()).hexdigest(), - hot_context_ref="", # Set by storage layer - relevant_resources=tuple(resources), - actor_state_ref=actor_state, - file_versions=file_versions or {}, - created_at=datetime.utcnow() - ) - ``` - - [ ] Commit: "feat(domain): add ContextSnapshot.capture() factory" - - [ ] **D1.3** [Hamza] Define `Decision` Pydantic model: - - [ ] **D1.3a** [Hamza] Create Decision class with identity fields: - ```python - class Decision(BaseModel): - """A recorded decision point in plan execution.""" - - model_config = ConfigDict(frozen=True) - - # Identity - decision_id: str = Field(..., description="ULID identifier") - plan_id: str = Field(..., description="Parent plan ULID") - - # Tree structure - parent_decision_id: str | None = Field( - default=None, description="Parent in decision tree" - ) - sequence_number: int = Field( - ..., ge=0, description="Order within plan (0=root)" - ) - ``` - - [ ] Commit: "feat(domain): add Decision model identity fields" - - [ ] **D1.3b** [Hamza] Add decision content fields: - ```python - # Decision content - decision_type: DecisionType = Field(..., description="Classification") - question: str = Field(..., min_length=1, description="What was decided") - chosen_option: str = Field(..., min_length=1, description="The choice made") - alternatives_considered: list[str] = Field( - default_factory=list, description="Other options evaluated" - ) - confidence_score: float | None = Field( - default=None, ge=0.0, le=1.0, description="AI confidence 0.0-1.0" - ) - rationale: str = Field(default="", description="Why this choice") - actor_reasoning: str | None = Field( - default=None, description="Raw LLM chain-of-thought" - ) - ``` - - [ ] Commit: "feat(domain): add Decision content fields" - - [ ] **D1.3c** [Hamza] Add context and relationship fields: - ```python - # Context for replay - context_snapshot: ContextSnapshot = Field( - ..., description="Snapshot at decision time" - ) - checkpoint_id: str | None = Field( - default=None, description="Sandbox checkpoint for rollback" - ) - - # Downstream relationships (populated during execution) - downstream_decision_ids: list[str] = Field( - default_factory=list, description="Decisions that depend on this" - ) - downstream_plan_ids: list[str] = Field( - default_factory=list, description="Subplans spawned from this" - ) - artifacts_produced: list[str] = Field( - default_factory=list, description="Artifact IDs created" - ) - ``` - - [ ] Commit: "feat(domain): add Decision context and relationship fields" - - [ ] **D1.3d** [Hamza] Add correction tracking fields: - ```python - # Correction tracking - is_correction: bool = Field( - default=False, description="Is this a corrected decision" - ) - corrects_decision_id: str | None = Field( - default=None, description="Original decision this corrects" - ) - superseded_by: str | None = Field( - default=None, description="Decision that replaced this one" - ) - - # Timestamps - created_at: datetime = Field(default_factory=datetime.utcnow) - ``` - - [ ] Commit: "feat(domain): add Decision correction tracking fields" - - [ ] **D1.3e** [Hamza] Add validators: - ```python - @field_validator('decision_id', 'plan_id') - @classmethod - def validate_ulid(cls, v: str) -> str: - """Validate ULID format.""" - if len(v) != 26 or not v.isalnum(): - raise ValueError(f"Invalid ULID format: {v}") - return v - - @model_validator(mode='after') - def validate_correction_consistency(self) -> Self: - """Ensure correction fields are consistent.""" - if self.is_correction and not self.corrects_decision_id: - raise ValueError("Correction must specify corrects_decision_id") - if self.corrects_decision_id and not self.is_correction: - raise ValueError("corrects_decision_id requires is_correction=True") - return self - ``` - - [ ] Commit: "feat(domain): add Decision validators" - - [ ] **D1.4** [Hamza] Add Decision helper methods: - - [ ] **D1.4a** [Hamza] Add computed properties: - ```python - @property - def is_root(self) -> bool: - """Check if this is the root decision (no parent).""" - return self.parent_decision_id is None - - @property - def is_superseded(self) -> bool: - """Check if this decision has been replaced.""" - return self.superseded_by is not None - - @property - def has_downstream_work(self) -> bool: - """Check if this decision spawned work.""" - return bool(self.downstream_decision_ids or self.downstream_plan_ids) - - @property - def summary(self) -> str: - """Short summary for display.""" - q = self.question[:50] + "..." if len(self.question) > 50 else self.question - return f"[{self.decision_type.value}] {q}" - ``` - - [ ] Commit: "feat(domain): add Decision computed properties" - - [ ] **D1.4b** [Hamza] Add mutation methods (return new instance): - ```python - def with_downstream_decision(self, decision_id: str) -> "Decision": - """Return new Decision with added downstream decision.""" - return self.model_copy(update={ - "downstream_decision_ids": [*self.downstream_decision_ids, decision_id] - }) - - def with_downstream_plan(self, plan_id: str) -> "Decision": - """Return new Decision with added downstream plan.""" - return self.model_copy(update={ - "downstream_plan_ids": [*self.downstream_plan_ids, plan_id] - }) - - def with_artifact(self, artifact_id: str) -> "Decision": - """Return new Decision with added artifact.""" - return self.model_copy(update={ - "artifacts_produced": [*self.artifacts_produced, artifact_id] - }) - - def mark_superseded(self, by_decision_id: str) -> "Decision": - """Return new Decision marked as superseded.""" - return self.model_copy(update={"superseded_by": by_decision_id}) - ``` - - [ ] Commit: "feat(domain): add Decision mutation methods" - - [ ] Tests: Behave scenarios for decision model - - [ ] **D1.5** [Rui] Write Behave scenarios in `features/decision_model.feature`: - - [ ] **D1.5a** [Rui] Creation scenarios: - - [ ] Scenario: Create decision with all required fields - - [ ] Given valid decision_id, plan_id, question, chosen_option, context_snapshot - - [ ] When I create a Decision with these fields - - [ ] Then the Decision is created successfully - - [ ] And sequence_number defaults to provided value - - [ ] Scenario: Create root decision (no parent) - - [ ] When I create a Decision with parent_decision_id=None - - [ ] Then is_root property returns True - - [ ] Scenario: Create child decision - - [ ] When I create a Decision with parent_decision_id set - - [ ] Then is_root property returns False - - [ ] Commit: "test(behave): add decision creation scenarios" - - [ ] **D1.5b** [Rui] Validation scenarios: - - [ ] Scenario: Invalid ULID format rejected - - [ ] When I create a Decision with decision_id="invalid" - - [ ] Then validation error is raised - - [ ] Scenario: Confidence score must be 0.0-1.0 - - [ ] When I create a Decision with confidence_score=1.5 - - [ ] Then validation error is raised - - [ ] Scenario: Correction without corrects_decision_id fails - - [ ] When I create a Decision with is_correction=True and corrects_decision_id=None - - [ ] Then validation error mentions correction consistency - - [ ] Commit: "test(behave): add decision validation scenarios" - - [ ] **D1.5c** [Rui] DecisionType scenarios: - - [ ] Scenario: Each decision type validates correctly - - [ ] For each DecisionType enum value - - [ ] When I create a Decision with that type - - [ ] Then decision is created successfully - - [ ] Scenario: is_strategy_decision helper works - - [ ] Given DecisionType.STRATEGY_CHOICE - - [ ] Then DecisionType.is_strategy_decision() returns True - - [ ] Given DecisionType.TOOL_INVOCATION - - [ ] Then DecisionType.is_strategy_decision() returns False - - [ ] Commit: "test(behave): add DecisionType scenarios" - - [ ] **D1.5d** [Rui] Context snapshot scenarios: - - [ ] Scenario: ContextSnapshot.capture() creates valid snapshot - - [ ] Given hot_context string and resource list - - [ ] When I call ContextSnapshot.capture() - - [ ] Then snapshot has valid ULID - - [ ] And hot_context_hash is SHA-256 of content - - [ ] Scenario: ContextSnapshot is immutable - - [ ] Given a ContextSnapshot instance - - [ ] When I try to modify a field - - [ ] Then FrozenInstanceError is raised - - [ ] Commit: "test(behave): add ContextSnapshot scenarios" +**Parallel Group D1: Decision Domain [Hamza + Rui]** (foundation for D2-D5) +- [ ] **COMMIT (Owner: Hamza | Group: D1.domain) - Commit message: "feat(domain): add decision model and context snapshots"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Add `DecisionType`, `ContextSnapshot`, and `Decision` models with correction fields and helpers. + - [ ] Code [Hamza]: Include required fields: question, chosen option, alternatives, confidence score, rationale, dependencies, and context hash. + - [ ] Docs [Hamza]: Add `docs/reference/decision_model.md` with examples and schema notes. + - [ ] Tests (Behave) [Rui]: Add `features/decision_model.feature` for validation and helpers. + - [ ] Tests (Robot) [Rui]: Add `robot/decision_model.robot` smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_model_bench.py` for decision validation throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(domain): add decision model and context snapshots"`. -- [ ] **Stage D2: Decision Recording** (Day 16-18) **[Hamza]** - - **SEQUENTIAL ORDER**: D2.1 (Service scaffold) → D2.2 (record_decision) → D2.3 (tree queries) → D2.4 (context capture) → D2.5 (strategy integration) → D2.6 (downstream updates) → D2.7 (Tests) - - - [ ] Code: Record decisions during Strategize - - [ ] **D2.1** [Hamza] Create `DecisionService` scaffold in `src/cleveragents/application/services/decision_service.py`: - - [ ] **D2.1a** [Hamza] Define service class with dependencies: - ```python - class DecisionService: - """Service for recording and querying decisions.""" - - def __init__( - self, - decision_repo: DecisionRepository, - snapshot_store: ContextSnapshotStore, - plan_repo: LifecyclePlanRepository - ): - self._decision_repo = decision_repo - self._snapshot_store = snapshot_store - self._plan_repo = plan_repo - self._sequence_counters: dict[str, int] = {} # plan_id -> next sequence - ``` - - [ ] Commit: "feat(service): add DecisionService scaffold" - - [ ] **D2.1b** [Hamza] Define ContextSnapshotStore protocol: - ```python - class ContextSnapshotStore(Protocol): - """Protocol for storing context snapshots.""" - - def store(self, snapshot: ContextSnapshot, content: str) -> ContextSnapshot: - """Store snapshot content and return with ref set.""" - ... - - def retrieve(self, snapshot_id: str) -> tuple[ContextSnapshot, str]: - """Retrieve snapshot and its content.""" - ... - - def retrieve_by_hash(self, hash: str) -> tuple[ContextSnapshot, str] | None: - """Retrieve by content hash (for deduplication).""" - ... - ``` - - [ ] Commit: "feat(service): define ContextSnapshotStore protocol" - - [ ] **D2.2** [Hamza] Implement `record_decision()` method: - - [ ] **D2.2a** [Hamza] Core implementation: - ```python - def record_decision( - self, - plan_id: str, - decision_type: DecisionType, - question: str, - chosen_option: str, - hot_context: str, - resources: list[str], - parent_decision_id: str | None = None, - alternatives: list[str] | None = None, - confidence: float | None = None, - rationale: str = "", - actor_reasoning: str | None = None, - checkpoint_id: str | None = None - ) -> Decision: - """Record a new decision for a plan.""" - import ulid - - # Generate IDs - decision_id = ulid.new().str - - # Get next sequence number for this plan - seq = self._get_next_sequence(plan_id) - - # Capture context snapshot - snapshot = self._capture_snapshot(hot_context, resources, checkpoint_id) - - # Create decision - decision = Decision( - decision_id=decision_id, - plan_id=plan_id, - parent_decision_id=parent_decision_id, - sequence_number=seq, - decision_type=decision_type, - question=question, - chosen_option=chosen_option, - alternatives_considered=alternatives or [], - confidence_score=confidence, - rationale=rationale, - actor_reasoning=actor_reasoning, - context_snapshot=snapshot, - checkpoint_id=checkpoint_id - ) - - # Persist - self._decision_repo.create(decision) - - # Update parent's downstream if applicable - if parent_decision_id: - self._add_downstream_decision(parent_decision_id, decision_id) - - logger.info(f"Recorded decision {decision_id}: {decision.summary}") - return decision - ``` - - [ ] Commit: "feat(service): implement record_decision()" - - [ ] **D2.2b** [Hamza] Add sequence number management: - ```python - def _get_next_sequence(self, plan_id: str) -> int: - """Get next sequence number for a plan.""" - if plan_id not in self._sequence_counters: - # Load max sequence from existing decisions - existing = self._decision_repo.get_max_sequence(plan_id) - self._sequence_counters[plan_id] = (existing or -1) + 1 - - seq = self._sequence_counters[plan_id] - self._sequence_counters[plan_id] += 1 - return seq - ``` - - [ ] Commit: "feat(service): add sequence number management" - - [ ] **D2.3** [Hamza] Implement tree query methods: - - [ ] **D2.3a** [Hamza] Implement `get_decision_tree()`: - ```python - def get_decision_tree(self, plan_id: str) -> list[Decision]: - """Get all decisions for a plan in tree order.""" - decisions = self._decision_repo.get_by_plan(plan_id) - - # Sort by sequence number to get chronological order - return sorted(decisions, key=lambda d: d.sequence_number) - - def get_decision_tree_nested(self, plan_id: str) -> DecisionTree: - """Get decisions as nested tree structure.""" - decisions = self.get_decision_tree(plan_id) - return self._build_tree(decisions) - - def _build_tree(self, decisions: list[Decision]) -> DecisionTree: - """Build tree from flat list of decisions.""" - by_id = {d.decision_id: d for d in decisions} - roots = [] - - for d in decisions: - if d.parent_decision_id is None: - roots.append(DecisionNode(decision=d, children=[])) - else: - # Find parent and add as child - # Implementation details... - - return DecisionTree(roots=roots, total_count=len(decisions)) - ``` - - [ ] Commit: "feat(service): implement decision tree queries" - - [ ] **D2.3b** [Hamza] Implement `get_decision()` and `get_children()`: - ```python - def get_decision(self, decision_id: str) -> Decision | None: - """Get a single decision by ID.""" - return self._decision_repo.get_by_id(decision_id) - - def get_children(self, decision_id: str) -> list[Decision]: - """Get all direct children of a decision.""" - return self._decision_repo.get_children(decision_id) - - def get_ancestors(self, decision_id: str) -> list[Decision]: - """Get all ancestors from decision to root.""" - ancestors = [] - current = self.get_decision(decision_id) - - while current and current.parent_decision_id: - parent = self.get_decision(current.parent_decision_id) - if parent: - ancestors.append(parent) - current = parent - - return ancestors - ``` - - [ ] Commit: "feat(service): implement get_decision and get_children" - - [ ] **D2.4** [Hamza] Implement context snapshot capture: - - [ ] **D2.4a** [Hamza] Implement `_capture_snapshot()`: - ```python - def _capture_snapshot( - self, - hot_context: str, - resources: list[str], - checkpoint_id: str | None = None - ) -> ContextSnapshot: - """Capture and store a context snapshot.""" - # Create snapshot object - snapshot = ContextSnapshot.capture( - hot_context=hot_context, - resources=resources, - actor_state=checkpoint_id - ) - - # Check for duplicate by hash (deduplication) - existing = self._snapshot_store.retrieve_by_hash(snapshot.hot_context_hash) - if existing: - logger.debug(f"Reusing existing snapshot with hash {snapshot.hot_context_hash[:8]}") - return existing[0] - - # Store new snapshot - stored = self._snapshot_store.store(snapshot, hot_context) - return stored - ``` - - [ ] Commit: "feat(service): implement context snapshot capture" - - [ ] **D2.4b** [Hamza] Implement FileContextSnapshotStore: - ```python - class FileContextSnapshotStore: - """Store snapshots in filesystem.""" - - def __init__(self, base_dir: Path): - self._base_dir = base_dir - self._base_dir.mkdir(parents=True, exist_ok=True) - - def store(self, snapshot: ContextSnapshot, content: str) -> ContextSnapshot: - """Store snapshot content to file.""" - file_path = self._base_dir / f"{snapshot.snapshot_id}.json" - - data = { - "snapshot": snapshot.__dict__, - "content": content - } - file_path.write_text(json.dumps(data)) - - # Update snapshot with ref - return dataclasses.replace( - snapshot, - hot_context_ref=str(file_path) - ) - ``` - - [ ] Commit: "feat(service): implement FileContextSnapshotStore" - - [ ] **D2.5** [Hamza] Integrate decision recording into strategy actor: - - [ ] **D2.5a** [Hamza] Create DecisionRecordingCallback: - ```python - class DecisionRecordingCallback: - """LangGraph callback to record decisions during execution.""" - - def __init__(self, decision_service: DecisionService, plan_id: str): - self._service = decision_service - self._plan_id = plan_id - self._current_parent: str | None = None - - def on_strategy_decision( - self, - question: str, - chosen: str, - alternatives: list[str], - confidence: float | None, - rationale: str, - context: str - ) -> Decision: - """Called when strategy actor makes a decision.""" - decision = self._service.record_decision( - plan_id=self._plan_id, - decision_type=DecisionType.STRATEGY_CHOICE, - question=question, - chosen_option=chosen, - hot_context=context, - resources=[], # Populated from plan - parent_decision_id=self._current_parent, - alternatives=alternatives, - confidence=confidence, - rationale=rationale - ) - return decision - ``` - - [ ] Commit: "feat(service): add DecisionRecordingCallback" - - [ ] **D2.5b** [Hamza] Record root PROMPT_DEFINITION decision: - ```python - def record_prompt_definition( - self, - plan_id: str, - prompt: str, - context: str - ) -> Decision: - """Record the initial prompt as root decision.""" - return self.record_decision( - plan_id=plan_id, - decision_type=DecisionType.PROMPT_DEFINITION, - question="What should be done?", - chosen_option=prompt, - hot_context=context, - resources=[], - parent_decision_id=None, - rationale="User provided prompt" - ) - ``` - - [ ] Commit: "feat(service): add record_prompt_definition()" - - [ ] **D2.6** [Hamza] Implement downstream relationship updates: - - [ ] **D2.6a** [Hamza] Add methods to update downstream fields: - ```python - def _add_downstream_decision(self, parent_id: str, child_id: str) -> None: - """Add child to parent's downstream_decision_ids.""" - parent = self._decision_repo.get_by_id(parent_id) - if parent: - updated = parent.with_downstream_decision(child_id) - self._decision_repo.update(updated) - - def add_downstream_plan(self, decision_id: str, plan_id: str) -> None: - """Record that a decision spawned a subplan.""" - decision = self._decision_repo.get_by_id(decision_id) - if decision: - updated = decision.with_downstream_plan(plan_id) - self._decision_repo.update(updated) - logger.info(f"Linked subplan {plan_id} to decision {decision_id}") - - def add_artifact(self, decision_id: str, artifact_id: str) -> None: - """Record that a decision produced an artifact.""" - decision = self._decision_repo.get_by_id(decision_id) - if decision: - updated = decision.with_artifact(artifact_id) - self._decision_repo.update(updated) - ``` - - [ ] Commit: "feat(service): implement downstream relationship updates" - - [ ] **D2.6b** [Hamza] Implement `mark_superseded()`: - ```python - def mark_superseded(self, decision_id: str, by_decision_id: str) -> None: - """Mark a decision as superseded by another.""" - decision = self._decision_repo.get_by_id(decision_id) - if not decision: - raise DecisionNotFoundError(decision_id) - - if decision.superseded_by: - raise AlreadySupersededError( - f"Decision {decision_id} already superseded by {decision.superseded_by}" - ) - - updated = decision.mark_superseded(by_decision_id) - self._decision_repo.update(updated) - logger.info(f"Marked decision {decision_id} as superseded by {by_decision_id}") - ``` - - [ ] Commit: "feat(service): implement mark_superseded()" - - [ ] Tests: Verify decision tree is built during Strategize - - [ ] **D2.7** [Rui] Write Behave scenarios in `features/decision_recording.feature`: - - [ ] **D2.7a** [Rui] Basic recording scenarios: - - [ ] Scenario: Record first decision creates root - - [ ] Given a plan "plan-123" with no decisions - - [ ] When I call record_decision with decision_type=PROMPT_DEFINITION - - [ ] Then a Decision is created with sequence_number=0 - - [ ] And parent_decision_id is None - - [ ] And is_root returns True - - [ ] Scenario: Record subsequent decisions increment sequence - - [ ] Given a plan with 2 existing decisions - - [ ] When I record another decision - - [ ] Then sequence_number is 2 - - [ ] Commit: "test(behave): add basic decision recording scenarios" - - [ ] **D2.7b** [Rui] Tree structure scenarios: - - [ ] Scenario: Child decision links to parent - - [ ] Given root decision D1 exists - - [ ] When I record decision D2 with parent_decision_id=D1.id - - [ ] Then D1.downstream_decision_ids contains D2.id - - [ ] Scenario: Get decision tree returns correct order - - [ ] Given decisions D1, D2, D3 with sequences 0, 1, 2 - - [ ] When I call get_decision_tree(plan_id) - - [ ] Then decisions are returned in sequence order - - [ ] Scenario: Get children returns direct children only - - [ ] Given D1 -> D2 -> D3 (D2 child of D1, D3 child of D2) - - [ ] When I call get_children(D1.id) - - [ ] Then only D2 is returned (not D3) - - [ ] Commit: "test(behave): add decision tree structure scenarios" - - [ ] **D2.7c** [Rui] Context snapshot scenarios: - - [ ] Scenario: Context snapshot captured with decision - - [ ] Given hot context "file contents..." - - [ ] When I record a decision - - [ ] Then decision.context_snapshot is not None - - [ ] And context_snapshot.hot_context_hash is valid SHA-256 - - [ ] Scenario: Duplicate context reuses existing snapshot - - [ ] Given decision D1 with context hash "abc123" - - [ ] When I record D2 with identical context - - [ ] Then D2.context_snapshot.snapshot_id differs from D1 - - [ ] But content is only stored once (deduplication) - - [ ] Commit: "test(behave): add context snapshot scenarios" - - [ ] **D2.7d** [Rui] Downstream relationship scenarios: - - [ ] Scenario: Subplan spawn updates downstream_plan_ids - - [ ] Given decision D1 of type SUBPLAN_SPAWN - - [ ] When subplan SP1 is created from D1 - - [ ] And add_downstream_plan(D1.id, SP1.id) is called - - [ ] Then D1.downstream_plan_ids contains SP1.id - - [ ] Scenario: Artifact production updates artifacts_produced - - [ ] Given decision D1 produces artifact A1 - - [ ] When add_artifact(D1.id, A1.id) is called - - [ ] Then D1.artifacts_produced contains A1.id - - [ ] Commit: "test(behave): add downstream relationship scenarios" +**Parallel Group D2: Decision Recording Service [Hamza + Luis]** (depends on D1) +- [ ] **COMMIT (Owner: Hamza | Group: D2.service) - Commit message: "feat(service): add decision recording and snapshot store"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement `DecisionService` with `record_decision`, sequence numbers, tree queries, and downstream linking. + - [ ] Code [Hamza]: Add `ContextSnapshotStore` interface with a file-backed MVP implementation and hash dedupe. + - [ ] Code [Luis]: Integrate decision recording into strategize/execute phases (prompt/strategy/subplan/tool decisions). + - [ ] Docs [Hamza]: Add `docs/reference/decision_service.md` covering recording and snapshots. + - [ ] Tests (Behave) [Rui]: Add `features/decision_recording.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add `robot/decision_recording.robot` integration smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_recording_bench.py` for record throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(service): add decision recording and snapshot store"`. -- [ ] **Stage D3: Decision CLI & Viewing** (Day 16-17) **[Hamza]** - - **SEQUENTIAL ORDER**: D3.1 (tree command) → D3.2 (explain command) → D3.3 (JSON output) → D3.4 (guidance-file) → D3.5 (Tests) - - - [ ] Code: Decision viewing commands - - [ ] **D3.1** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan tree [plan_id]`: - - [ ] **D3.1a** [Hamza] Create command in `src/cleveragents/cli/commands/plan.py`: - ```python - @plan.command("tree") - @click.argument("plan_id", required=False) - @click.option("--format", "output_format", - type=click.Choice(["tree", "json", "flat"]), default="tree") - @click.option("--show-superseded", is_flag=True, - help="Include superseded decisions") - def show_tree(plan_id: str | None, output_format: str, show_superseded: bool): - """Display the decision tree for a plan.""" - ``` - - [ ] Commit: "feat(cli): add plan tree command signature" - - [ ] **D3.1b** [Hamza] Implement plan resolution: - ```python - # If no plan_id, use current/most recent plan - if not plan_id: - plan = plan_service.get_current_plan() - if not plan: - console.print("[red]No active plan. Specify a plan ID.[/red]") - raise SystemExit(1) - plan_id = plan.plan_id - - # Fetch decision tree - decisions = decision_service.get_decision_tree(plan_id) - if not decisions: - console.print(f"[yellow]No decisions recorded for plan {plan_id}[/yellow]") - return - ``` - - [ ] Commit: "feat(cli): implement plan resolution for tree command" - - [ ] **D3.1c** [Hamza] Implement tree rendering with Rich: - ```python - def _render_decision_tree(decisions: list[Decision], show_superseded: bool): - """Render decision tree using Rich Tree.""" - from rich.tree import Tree - from rich.text import Text - - # Build tree structure - root_decisions = [d for d in decisions if d.is_root] - by_parent: dict[str, list[Decision]] = {} - for d in decisions: - if d.parent_decision_id: - by_parent.setdefault(d.parent_decision_id, []).append(d) - - # Create Rich tree - tree = Tree("[bold]Decision Tree[/bold]") - - def add_node(parent_tree, decision: Decision): - # Format decision display - type_color = _get_type_color(decision.decision_type) - label = Text() - label.append(f"[{decision.decision_type.value}] ", style=type_color) - label.append(f'"{decision.question[:40]}..."' if len(decision.question) > 40 else f'"{decision.question}"') - - if decision.confidence_score: - label.append(f" (conf: {decision.confidence_score:.2f})", style="dim") - - # Mark superseded - if decision.superseded_by: - if not show_superseded: - return - label.stylize("strike dim") - label.append(" [SUPERSEDED]", style="yellow") - - # Mark corrections - if decision.is_correction: - label.append(" [CORRECTION]", style="green") - - # Add subplan links - for subplan_id in decision.downstream_plan_ids: - label.append(f" → {subplan_id[:8]}", style="cyan") - - node = parent_tree.add(label) - - # Add children recursively - for child in by_parent.get(decision.decision_id, []): - add_node(node, child) - - for root in root_decisions: - add_node(tree, root) - - console.print(tree) - ``` - - [ ] Commit: "feat(cli): implement tree rendering with Rich" - - [ ] **D3.1d** [Hamza] Add type-specific coloring: - ```python - def _get_type_color(decision_type: DecisionType) -> str: - """Get color for decision type.""" - colors = { - DecisionType.PROMPT_DEFINITION: "bold white", - DecisionType.STRATEGY_CHOICE: "blue", - DecisionType.IMPLEMENTATION_CHOICE: "cyan", - DecisionType.RESOURCE_SELECTION: "magenta", - DecisionType.SUBPLAN_SPAWN: "green", - DecisionType.TOOL_INVOCATION: "yellow", - DecisionType.ERROR_RECOVERY: "red", - DecisionType.VALIDATION_RESPONSE: "orange3", - DecisionType.USER_INTERVENTION: "bold yellow", - } - return colors.get(decision_type, "white") - ``` - - [ ] Commit: "feat(cli): add decision type coloring" - - [ ] **D3.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan explain `: - - [ ] **D3.2a** [Hamza] Create command signature: - ```python - @plan.command("explain") - @click.argument("decision_id") - @click.option("--show-context", is_flag=True, help="Show full context snapshot") - @click.option("--show-reasoning", is_flag=True, help="Show raw LLM reasoning") - def explain_decision(decision_id: str, show_context: bool, show_reasoning: bool): - """Show detailed explanation of a specific decision.""" - ``` - - [ ] Commit: "feat(cli): add plan explain command signature" - - [ ] **D3.2b** [Hamza] Implement detailed display: - ```python - decision = decision_service.get_decision(decision_id) - if not decision: - console.print(f"[red]Decision {decision_id} not found[/red]") - raise SystemExit(1) - - # Create panels for display - from rich.panel import Panel - from rich.table import Table - - # Header panel - header = Panel( - f"[bold]Decision: {decision.decision_id}[/bold]\n" - f"Type: {decision.decision_type.value}\n" - f"Plan: {decision.plan_id}", - title="Decision Details" - ) - console.print(header) - - # Question and answer - console.print(f"\n[bold]Question:[/bold] {decision.question}") - console.print(f"\n[bold green]Chosen:[/bold green] {decision.chosen_option}") - - # Alternatives - if decision.alternatives_considered: - console.print("\n[bold]Alternatives Considered:[/bold]") - for alt in decision.alternatives_considered: - console.print(f" • {alt}") - - # Confidence and rationale - if decision.confidence_score is not None: - bar = "█" * int(decision.confidence_score * 10) + "░" * (10 - int(decision.confidence_score * 10)) - console.print(f"\n[bold]Confidence:[/bold] {decision.confidence_score:.2f} [{bar}]") - - if decision.rationale: - console.print(f"\n[bold]Rationale:[/bold] {decision.rationale}") - ``` - - [ ] Commit: "feat(cli): implement explain decision display" - - [ ] **D3.2c** [Hamza] Show upstream/downstream relationships: - ```python - # Upstream (ancestors) - ancestors = decision_service.get_ancestors(decision_id) - if ancestors: - console.print("\n[bold]Decision Path (what led here):[/bold]") - for i, anc in enumerate(reversed(ancestors)): - indent = " " * i - console.print(f"{indent}↳ [{anc.decision_type.value}] {anc.question[:50]}") - - # Downstream impact - children = decision_service.get_children(decision_id) - if children or decision.downstream_plan_ids: - console.print("\n[bold]Downstream Impact:[/bold]") - if children: - console.print(f" • {len(children)} child decisions") - if decision.downstream_plan_ids: - console.print(f" • {len(decision.downstream_plan_ids)} subplans spawned:") - for sp_id in decision.downstream_plan_ids: - console.print(f" - {sp_id}") - if decision.artifacts_produced: - console.print(f" • {len(decision.artifacts_produced)} artifacts produced") - ``` - - [ ] Commit: "feat(cli): add upstream/downstream display" - - [ ] **D3.2d** [Hamza] Add context and reasoning display: - ```python - # Context snapshot - if show_context: - console.print("\n[bold]Context Snapshot:[/bold]") - console.print(f" Hash: {decision.context_snapshot.hot_context_hash[:16]}...") - console.print(f" Resources: {', '.join(decision.context_snapshot.relevant_resources)}") - - # Optionally show full content - try: - _, content = snapshot_store.retrieve(decision.context_snapshot.snapshot_id) - console.print(Panel(content[:1000] + "..." if len(content) > 1000 else content, - title="Context Content")) - except Exception as e: - console.print(f" [dim]Content not available: {e}[/dim]") - - # Raw LLM reasoning - if show_reasoning and decision.actor_reasoning: - console.print(Panel(decision.actor_reasoning, title="LLM Reasoning")) - ``` - - [ ] Commit: "feat(cli): add context and reasoning display" - - [ ] **D3.3** [Hamza] Implement JSON output: - - [ ] **D3.3a** [Hamza] Add JSON format to tree command: - ```python - if output_format == "json": - # Build JSON structure - def decision_to_dict(d: Decision) -> dict: - return { - "decision_id": d.decision_id, - "type": d.decision_type.value, - "question": d.question, - "chosen_option": d.chosen_option, - "alternatives": d.alternatives_considered, - "confidence": d.confidence_score, - "rationale": d.rationale, - "parent_id": d.parent_decision_id, - "sequence": d.sequence_number, - "is_correction": d.is_correction, - "superseded_by": d.superseded_by, - "downstream_decisions": d.downstream_decision_ids, - "downstream_plans": d.downstream_plan_ids, - "created_at": d.created_at.isoformat() - } - - tree_json = { - "plan_id": plan_id, - "decision_count": len(decisions), - "decisions": [decision_to_dict(d) for d in decisions] - } - - print(json.dumps(tree_json, indent=2)) - ``` - - [ ] Commit: "feat(cli): add JSON output for plan tree" - - [ ] **D3.3b** [Hamza] Add flat format (for scripting): - ```python - if output_format == "flat": - # Tab-separated values for easy parsing - print("ID\tTYPE\tSEQ\tPARENT\tQUESTION") - for d in decisions: - print(f"{d.decision_id}\t{d.decision_type.value}\t{d.sequence_number}\t" - f"{d.parent_decision_id or '-'}\t{d.question[:50]}") - ``` - - [ ] Commit: "feat(cli): add flat output for plan tree" - - [ ] **D3.4** [Hamza] Implement `--guidance-file` option: - - [ ] **D3.4a** [Hamza] Add option to plan correct command: - ```python - @plan.command("correct") - @click.argument("decision_id") - @click.option("--mode", type=click.Choice(["revert", "append"]), required=True) - @click.option("--guidance", "-g", help="Correction guidance text") - @click.option("--guidance-file", "-f", type=click.File('r'), - help="Read guidance from file (use - for stdin)") - @click.option("--dry-run", is_flag=True, help="Show impact without executing") - def correct_decision(decision_id, mode, guidance, guidance_file, dry_run): - """Correct a decision and re-execute affected work.""" - ``` - - [ ] Commit: "feat(cli): add guidance-file option to correct command" - - [ ] **D3.4b** [Hamza] Handle guidance source priority: - ```python - # Get guidance from appropriate source - if guidance_file: - guidance_text = guidance_file.read() - elif guidance: - guidance_text = guidance - else: - console.print("[red]Either --guidance or --guidance-file is required[/red]") - raise SystemExit(1) - - if not guidance_text.strip(): - console.print("[red]Guidance cannot be empty[/red]") - raise SystemExit(1) - ``` - - [ ] Commit: "feat(cli): implement guidance source handling" - - [ ] Tests: Behave scenarios for decision CLI - - [ ] **D3.5** [Rui] Write Behave scenarios in `features/decision_cli.feature`: - - [ ] **D3.5a** [Rui] Tree command scenarios: - - [ ] Scenario: Display decision tree for plan - - [ ] Given plan with 5 decisions in tree structure - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan tree {plan_id}` - - [ ] Then output shows tree with all decisions - - [ ] And decisions are color-coded by type - - [ ] Scenario: Tree command with JSON format - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan tree {plan_id} --format=json` - - [ ] Then output is valid JSON - - [ ] And JSON contains all decision fields - - [ ] Scenario: Tree hides superseded by default - - [ ] Given decision D1 superseded by D1' - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan tree` - - [ ] Then D1 is not shown - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan tree --show-superseded` - - [ ] Then D1 is shown with strikethrough - - [ ] Commit: "test(behave): add tree command scenarios" - - [ ] **D3.5b** [Rui] Explain command scenarios: - - [ ] Scenario: Explain shows full decision details - - [ ] Given decision D1 with all fields populated - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan explain {D1.id}` - - [ ] Then output shows question, chosen option, alternatives - - [ ] And output shows confidence and rationale - - [ ] Scenario: Explain shows upstream path - - [ ] Given decision D3 with ancestors D1 -> D2 -> D3 - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan explain {D3.id}` - - [ ] Then output shows "Decision Path" section - - [ ] And D1 and D2 are listed as ancestors - - [ ] Scenario: Explain shows downstream impact - - [ ] Given decision D1 with 2 child decisions and 1 subplan - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan explain {D1.id}` - - [ ] Then output shows "Downstream Impact" section - - [ ] And shows "2 child decisions" and "1 subplan" - - [ ] Commit: "test(behave): add explain command scenarios" - - [ ] **D3.5c** [Rui] Guidance file scenarios: - - [ ] Scenario: Read guidance from file - - [ ] Given guidance file with text "Fix the authentication bug" - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan correct {id} --mode=append --guidance-file guidance.txt` - - [ ] Then correction uses the file content as guidance - - [ ] Scenario: Read guidance from stdin - - [ ] When I run `echo "Fix bug" | agents [--data-dir PATH] [--config-path PATH] plan correct {id} --mode=append --guidance-file=-` - - [ ] Then correction uses stdin content as guidance - - [ ] Commit: "test(behave): add guidance file scenarios" +**Parallel Group D3: Decision CLI & Viewing [Hamza + Rui]** (depends on D1/D2) +- [ ] **COMMIT (Owner: Hamza | Group: D3.cli) - Commit message: "feat(cli): add plan tree and explain commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement `plan tree` and `plan explain` with rich/json/flat formats and `--show-superseded`/`--show-context`. + - [ ] Code [Hamza]: Add `--show-reasoning` to include confidence and alternatives in explain output per spec. + - [ ] Docs [Hamza]: Update CLI reference for decision viewing commands. + - [ ] Tests (Behave) [Rui]: Add tree/explain scenarios including superseded handling. + - [ ] Tests (Robot) [Rui]: Add `robot/decision_cli.robot` smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_cli_bench.py` for tree rendering overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(cli): add plan tree and explain commands"`. -- [ ] **Stage D4: Decision Correction Mechanism** (Day 17-19) **[Jeff - CRITICAL FOR 30-DAY GOAL]** - - **IMPORTANCE**: This is the core mechanism that enables large project autonomy. Without decision correction, any mistake requires restarting from scratch. With it, users can guide the system to correct specific decisions and only recompute affected work. - - **SEQUENTIAL ORDER**: D4.1 (Service) → D4.2 (Sandbox Checkpoints) → D4.3 (Re-execution) → D4.4-D4.5 (CLI) - - - [ ] Code: Implement decision correction (core to large project autonomy) - - [ ] **D4.1** [Jeff] Implement correction service in `src/cleveragents/application/services/correction_service.py`: - - [ ] **D4.1a** [Jeff] Create service scaffold and types: - - [ ] Import necessary domain models (Decision, Plan, DecisionType) - - [ ] Import repositories (DecisionRepository, LifecyclePlanRepository) - - [ ] Define `CorrectionResult` dataclass: - - [ ] `success: bool` - Whether correction succeeded - - [ ] `correction_attempt_id: str` - ULID of the correction attempt - - [ ] `new_decision_id: str | None` - ID of new decision (for revert mode) - - [ ] `subplan_id: str | None` - ID of fix subplan (for append mode) - - [ ] `affected_decisions: list[str]` - IDs of invalidated decisions - - [ ] `affected_plans: list[str]` - IDs of invalidated subplans - - [ ] `affected_artifacts: list[str]` - IDs of invalidated artifacts - - [ ] `error: str | None` - Error message if failed - - [ ] Define `ImpactAnalysis` dataclass: - - [ ] `decision_id: str` - Decision being analyzed - - [ ] `downstream_decisions: list[str]` - All affected decisions - - [ ] `downstream_plans: list[str]` - All affected subplans - - [ ] `downstream_artifacts: list[str]` - All affected artifacts - - [ ] `total_tokens_to_recompute: int | None` - Estimated cost - - [ ] Create `CorrectionService` class with DI for repositories - - [ ] Commit: "feat(correction): add CorrectionService scaffold and types" - - [ ] **D4.1b** [Jeff] Implement `correct_decision_revert(decision_id: str, guidance: str) -> CorrectionResult`: - - [ ] **Step 1: Validation** - - [ ] Fetch decision from repository - - [ ] Raise `DecisionNotFoundError` if not exists - - [ ] Fetch parent plan - - [ ] Raise `PlanNotCorrectableError` if plan.phase == APPLIED - - [ ] Raise `PlanNotCorrectableError` if decision.superseded_by is not None (already corrected) - - [ ] **Step 2: Impact Analysis** - - [ ] Call `identify_downstream_impact(decision_id)` to find all affected entities - - [ ] Log: "Correction will affect {n} decisions, {m} subplans, {k} artifacts" - - [ ] **Step 3: Create Correction Attempt Record** - - [ ] Generate new ULID for correction_attempt_id - - [ ] Create `correction_attempts` record with: - - [ ] `attempt_id = correction_attempt_id` - - [ ] `plan_id = decision.plan_id` - - [ ] `original_decision_id = decision_id` - - [ ] `status = 'pending'` - - [ ] `guidance = guidance` - - [ ] `created_at = now()` - - [ ] Persist to database - - [ ] **Step 4: Archive Old Subtree** - - [ ] For each affected decision: - - [ ] Create copy in `archived_decisions` table with original values - - [ ] Store reference to correction_attempt_id - - [ ] For each affected artifact: - - [ ] Move file to archive location - - [ ] Update artifact record with archive path - - [ ] Log: "Archived {n} decisions and {k} artifacts" - - [ ] **Step 5: Invalidate Old Decisions** - - [ ] For each affected decision starting from decision_id: - - [ ] Set `superseded_by = None` (will be filled when new decision created) - - [ ] Mark in execution_log that decision is invalidated - - [ ] For each affected subplan: - - [ ] Set state to CANCELLED - - [ ] Rollback subplan sandboxes - - [ ] **Step 6: Create Correction Decision** - - [ ] Create new Decision with: - - [ ] `decision_id = new ULID` - - [ ] `plan_id = original.plan_id` - - [ ] `parent_decision_id = original.parent_decision_id` (same parent) - - [ ] `sequence_number = original.sequence_number` (replaces in sequence) - - [ ] `decision_type = original.decision_type` - - [ ] `is_correction = True` - - [ ] `corrects_decision_id = decision_id` - - [ ] `question = original.question` - - [ ] `chosen_option = guidance` (user's correction) - - [ ] `rationale = f"User correction: {guidance}"` - - [ ] Update original decision: `superseded_by = new_decision_id` - - [ ] Persist new decision - - [ ] **Step 7: Rollback Sandbox** - - [ ] Get checkpoint_id from original decision's context_snapshot - - [ ] Call `sandbox_manager.rollback_to_checkpoint(plan_id, checkpoint_id)` - - [ ] This restores sandbox state to before the decision was made - - [ ] **Step 8: Re-execute from Decision Point** - - [ ] Build re-execution context: - - [ ] Include all decisions up to (but not including) the corrected one - - [ ] Include the new correction decision - - [ ] Include the guidance as additional context - - [ ] Call appropriate phase handler: - - [ ] If decision was in Strategize: resume strategy actor - - [ ] If decision was in Execute: resume execution actor - - [ ] Let actor generate new downstream decisions - - [ ] Continue normal phase flow - - [ ] **Step 9: Finalize** - - [ ] Update correction_attempt: `status = 'completed'`, `new_decision_id = new_decision.decision_id` - - [ ] Increment plan.attempt counter - - [ ] Return CorrectionResult with all IDs and counts - - [ ] **Error Handling** - - [ ] If any step fails, update correction_attempt: `status = 'failed'`, `error = message` - - [ ] Do NOT rollback the archive (preserve for debugging) - - [ ] Return CorrectionResult with success=False, error message - - [ ] Commit: "feat(correction): implement correct_decision_revert()" - - [ ] **D4.1c** [Jeff] Implement `correct_decision_append(decision_id: str, guidance: str) -> CorrectionResult`: - - [ ] **Step 1: Validation** (same as revert, but less strict) - - [ ] Fetch decision and plan - - [ ] Raise error if plan already Applied (can't modify) - - [ ] **Step 2: Create Fix Subplan** - - [ ] Create new action (or use built-in "fix" action) with: - - [ ] Description based on guidance - - [ ] Target resources from original decision's scope - - [ ] Use PlanLifecycleService.use_action() to create subplan - - [ ] Set subplan.parent_plan_id to original plan - - [ ] Set subplan's prompt to include: - - [ ] Original decision context - - [ ] What went wrong (from guidance) - - [ ] Instructions to fix - - [ ] **Step 3: Link to Decision** - - [ ] Add subplan_id to original decision's downstream_plan_ids - - [ ] Create new decision record of type USER_INTERVENTION - - [ ] Store guidance and fix plan reference - - [ ] **Step 4: Execute Fix Plan** - - [ ] If automation level allows, start subplan execution - - [ ] Otherwise, return subplan_id for manual execution - - [ ] Return CorrectionResult with subplan_id - - [ ] Commit: "feat(correction): implement correct_decision_append()" - - [ ] **D4.1d** [Jeff] Implement `identify_downstream_impact(decision_id: str) -> ImpactAnalysis`: - - [ ] **Recursive Decision Collection** - - [ ] Start with decision_id - - [ ] Query all decisions where parent_decision_id = current - - [ ] Recursively process each child - - [ ] Also check downstream_decision_ids relationship (DAG) - - [ ] Collect all IDs in depth-first order - - [ ] **Subplan Collection** - - [ ] For each decision, check if decision_type == SUBPLAN_SPAWN - - [ ] If so, add downstream_plan_ids to affected plans - - [ ] Recursively get that subplan's decisions too - - [ ] **Artifact Collection** - - [ ] For each affected decision, get artifacts_produced list - - [ ] Deduplicate (same artifact may be referenced multiple times) - - [ ] **Cost Estimation** (optional) - - [ ] Estimate tokens by summing context sizes of affected decisions - - [ ] This helps user decide if correction is worth it - - [ ] Return ImpactAnalysis with all collected data - - [ ] Commit: "feat(correction): implement identify_downstream_impact()" - - [ ] **D4.2** [Jeff] Implement sandbox checkpointing for correction: - - [ ] **D4.2a** [Jeff] Extend SandboxManager to track checkpoints: - - [ ] Add `create_checkpoint(plan_id: str, label: str) -> str`: - - [ ] For git sandboxes: commit current state with checkpoint tag - - [ ] For filesystem sandboxes: snapshot directory (or use git worktree trick) - - [ ] Return checkpoint_id - - [ ] Add `list_checkpoints(plan_id: str) -> list[Checkpoint]`: - - [ ] Return all checkpoints for the plan in chronological order - - [ ] Commit: "feat(sandbox): add checkpoint creation to SandboxManager" - - [ ] **D4.2b** [Jeff] Store checkpoint ID with each decision: - - [ ] Update Decision model: add `checkpoint_id: str | None` field - - [ ] When decision is created, auto-create checkpoint - - [ ] Store checkpoint_id in decision record - - [ ] Commit: "feat(decision): track checkpoint_id per decision" - - [ ] **D4.2c** [Jeff] Implement `rollback_to_checkpoint(plan_id: str, checkpoint_id: str) -> None`: - - [ ] Find all sandboxes for plan_id - - [ ] For each sandbox: - - [ ] If git: `git reset --hard {checkpoint_tag}` - - [ ] If filesystem: restore from snapshot - - [ ] Clear any state beyond checkpoint - - [ ] Update sandbox status - - [ ] Commit: "feat(sandbox): implement rollback_to_checkpoint()" - - [ ] **D4.2d** [Jeff] Handle checkpoint cleanup: - - [ ] After successful apply, old checkpoints can be pruned - - [ ] Keep at least N most recent checkpoints for debugging - - [ ] Implement `prune_checkpoints(plan_id: str, keep_count: int) -> int` - - [ ] Commit: "feat(sandbox): implement checkpoint pruning" - - [ ] **D4.3** [Jeff] Implement re-execution from correction point: - - [ ] **D4.3a** [Jeff] Build context for resumed execution: - - [ ] Create `CorrectionContext` dataclass: - - [ ] `original_decision: Decision` - What was being corrected - - [ ] `correction_decision: Decision` - The new corrected decision - - [ ] `guidance: str` - User's correction text - - [ ] `prior_decisions: list[Decision]` - Decisions that remain valid - - [ ] `invalidated_decisions: list[Decision]` - For reference/diff - - [ ] Commit: "feat(correction): define CorrectionContext" - - [ ] **D4.3b** [Jeff] Inject correction context into actor: - - [ ] Update strategy/execution actor invocation to accept CorrectionContext - - [ ] Actor prompt includes: - - [ ] "You previously decided: {original_decision.chosen_option}" - - [ ] "This decision is being corrected because: {guidance}" - - [ ] "Please reconsider and make a new decision based on this feedback." - - [ ] Actor should acknowledge correction and proceed - - [ ] Commit: "feat(correction): inject correction context into actor" - - [ ] **D4.3c** [Jeff] Resume phase execution from decision point: - - [ ] If correction is in Strategize phase: - - [ ] Resume strategy actor from decision point - - [ ] Actor generates new downstream decisions - - [ ] Continue until Strategize complete - - [ ] If correction is in Execute phase: - - [ ] Resume execution actor - - [ ] Regenerate affected subplans - - [ ] Continue execution - - [ ] Normal Apply phase follows - - [ ] Commit: "feat(correction): implement re-execution from decision point" - - [ ] **D4.3d** [Jeff] Handle correction failures: - - [ ] If actor fails during re-execution: - - [ ] Update correction_attempt status to 'failed' - - [ ] Preserve both old and new state for debugging - - [ ] Allow user to try different guidance - - [ ] Max correction attempts per decision: 3 (configurable) - - [ ] Commit: "feat(correction): handle re-execution failures" - - [ ] **D4.4** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan correct --mode=revert --guidance ""`: - - [ ] **D4.4a** [Hamza] Create correction command in plan CLI: - - [ ] Add `@plan.command("correct")` with Click - - [ ] Required argument: `decision_id: str` - - [ ] Required option: `--mode: str` (choices: revert, append) - - [ ] Required option: `--guidance: str` or `--guidance-file: Path` - - [ ] Optional: `--dry-run` to show impact without executing - - [ ] Commit: "feat(cli): add plan correct command scaffold" - - [ ] **D4.4b** [Hamza] Implement command logic for revert mode: - - [ ] Validate decision_id format (ULID) - - [ ] If --dry-run: - - [ ] Call CorrectionService.identify_downstream_impact() - - [ ] Display impact analysis - - [ ] Exit without making changes - - [ ] Show confirmation prompt: "This will affect {n} decisions. Continue? [y/N]" - - [ ] Support `--yes` to bypass confirmation - - [ ] If confirmed (or --yes), call CorrectionService.correct_decision_revert() - - [ ] Display progress with Rich console: - - [ ] "[1/5] Analyzing impact..." - - [ ] "[2/5] Archiving old decisions..." - - [ ] "[3/5] Rolling back sandbox..." - - [ ] "[4/5] Re-executing from decision point..." - - [ ] "[5/5] Finalizing correction..." - - [ ] On success, display: - - [ ] New decision ID - - [ ] Count of regenerated decisions - - [ ] Diff summary (files changed old vs new) - - [ ] On failure, display error with recovery suggestions - - [ ] Commit: "feat(cli): implement plan correct revert mode" - - [ ] **D4.4c** [Hamza] Handle guidance-file option: - - [ ] If --guidance-file specified: - - [ ] Read file contents as guidance - - [ ] Support `-` for stdin: `cat guidance.txt | agents [--data-dir PATH] [--config-path PATH] plan correct ... --guidance-file=-` - - [ ] Validate guidance is not empty - - [ ] Commit: "feat(cli): add guidance-file support to plan correct" - - [ ] **D4.5** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan correct --mode=append --guidance ""`: - - [ ] **D4.5a** [Hamza] Implement append mode command: - - [ ] No confirmation needed (additive, not destructive) - - [ ] Call CorrectionService.correct_decision_append() - - [ ] Display created subplan ID - - [ ] If automation allows, show execution progress - - [ ] Otherwise show: "Fix subplan created: {subplan_id}. Run `agents [--data-dir PATH] [--config-path PATH] plan execute {subplan_id}` to apply fix." - - [ ] Commit: "feat(cli): implement plan correct append mode" - - [ ] Tests: Correction mechanism tests (CRITICAL for 30-day goal) - - [ ] **D4.6** [Rui] Write Behave scenarios in `features/decision_correction.feature`: - - [ ] **D4.6a** [Rui] Revert mode basic scenarios: - - [ ] Scenario: Correct early decision re-executes downstream work - - [ ] Given a plan with 3 decisions (D1 → D2 → D3) in sequence - - [ ] And D1 chose "use PostgreSQL" with downstream D2 choosing "use psycopg2" - - [ ] When I correct D1 with guidance "use SQLite instead" - - [ ] Then D1 is marked superseded - - [ ] And a new D1' is created with chosen_option "use SQLite" - - [ ] And D2, D3 are invalidated and regenerated - - [ ] And new D2' reflects SQLite (e.g., "use sqlite3") - - [ ] And the correction_attempt record shows status='completed' - - [ ] Commit: "test(behave): add basic revert correction scenario" - - [ ] **D4.6b** [Rui] Subplan invalidation scenarios: - - [ ] Scenario: Correct decision with subplans invalidates subplans - - [ ] Given a plan where D2 is a SUBPLAN_SPAWN decision - - [ ] And subplan SP1 was created from D2 - - [ ] And SP1 has completed some work - - [ ] When I correct D2 with new guidance - - [ ] Then SP1 is marked as CANCELLED - - [ ] And SP1's sandbox is rolled back - - [ ] And a new subplan SP2 is created based on new guidance - - [ ] And SP2 uses the corrected context - - [ ] Commit: "test(behave): add subplan invalidation correction scenario" - - [ ] **D4.6c** [Rui] Append mode scenarios: - - [ ] Scenario: Append mode creates fix subplan without modifying history - - [ ] Given a plan with D1, D2, D3 all completed - - [ ] And the outcome has a bug due to D2's decision - - [ ] When I correct D2 with mode=append and guidance "add error handling" - - [ ] Then D2 is NOT marked as superseded - - [ ] And a new fix subplan is created - - [ ] And the fix subplan's prompt includes the guidance - - [ ] And the fix subplan targets the same resources as D2 - - [ ] Commit: "test(behave): add append mode correction scenario" - - [ ] **D4.6d** [Rui] Safety and error scenarios: - - [ ] Scenario: Cannot correct decision in Applied plan - - [ ] Given a plan that has been Applied successfully - - [ ] When I try to correct any decision - - [ ] Then I receive error "Cannot correct decisions in an applied plan" - - [ ] And no changes are made - - [ ] Scenario: Cannot correct already-corrected decision - - [ ] Given decision D1 that was already corrected to D1' - - [ ] When I try to correct D1 again - - [ ] Then I receive error "Decision already superseded" - - [ ] And hint "Correct the replacement decision D1' instead" - - [ ] Commit: "test(behave): add correction safety scenarios" - - [ ] **D4.6e** [Rui] History preservation scenarios: - - [ ] Scenario: Correction preserves history for comparison - - [ ] Given I correct decision D1 with new guidance - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan tree {plan_id} --show-superseded` - - [ ] Then I see the original D1 decision details - - [ ] And I see the correction D1' decision details - - [ ] And I can compare the outcomes via `agents [--data-dir PATH] [--config-path PATH] plan diff --correction {plan_id}` - - [ ] Scenario: Archived artifacts are accessible - - [ ] Given correction archived some generated files - - [ ] Then I can retrieve archived files for diff comparison - - [ ] Commit: "test(behave): add history preservation scenarios" - - [ ] **D4.6f** [Rui] Dry-run and impact analysis scenarios: - - [ ] Scenario: Dry-run shows impact without making changes - - [ ] Given a plan with 5 decisions and 2 subplans - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] plan correct D2 --mode=revert --guidance "..." --dry-run` - - [ ] Then I see "This will affect:" - - [ ] And I see "- 3 decisions" - - [ ] And I see "- 1 subplan" - - [ ] And I see "- Estimated recomputation: ~5000 tokens" - - [ ] And no changes are made to the plan - - [ ] Commit: "test(behave): add dry-run and impact analysis scenarios" +**Parallel Group D4: Decision Correction [Jeff + Luis]** (depends on D2/D3) +- [ ] **COMMIT (Owner: Jeff | Group: D4.revert) - Commit message: "feat(service): add decision correction revert flow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Implement correction impact analysis and dry-run reporting. + - [ ] Code [Jeff]: Revert flow with checkpoint rollback, supersede downstream decisions, and subtree re-exec. + - [ ] Code [Jeff]: Persist correction attempt IDs and link them to superseded decisions. + - [ ] Docs [Jeff]: Add `docs/reference/decision_correction.md` for revert behavior. + - [ ] Tests (Behave) [Rui]: Add revert + dry-run scenarios. + - [ ] Tests (Robot) [Rui]: Add revert integration tests with checkpoint rollback. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_correction_revert_bench.py` for correction overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(service): add decision correction revert flow"`. +- [ ] **COMMIT (Owner: Jeff | Group: D4.append) - Commit message: "feat(service): add decision correction append flow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Append flow creating fix subplan without rewriting history; link correction attempt + decision tree updates. + - [ ] Code [Jeff]: Record append corrections as separate subtree with explicit lineage. + - [ ] Docs [Jeff]: Extend correction docs for append mode and guidance-file usage. + - [ ] Tests (Behave) [Rui]: Add append correction scenarios. + - [ ] Tests (Robot) [Rui]: Add append correction smoke test. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_correction_append_bench.py` for append overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(service): add decision correction append flow"`. -- [ ] **Stage D5: Decision Persistence** (Day 18-19) **[Hamza]** - - **SEQUENTIAL ORDER**: D5.1 (decisions table) → D5.2 (dependencies table) → D5.3 (correction_attempts) → D5.4 (context_snapshots) → D5.5 (DecisionModel) → D5.6 (DecisionRepository) → D5.7 (Tests) - - - [ ] Code: Decision database schema - - [ ] **D5.1** [Hamza] Create Alembic migration for `decisions` table: - - [ ] **D5.1a** [Hamza] Generate migration file: - - [ ] Run `alembic revision --autogenerate -m "create_decisions_table"` - - [ ] Commit: "chore(db): generate decisions table migration" - - [ ] **D5.1b** [Hamza] Define schema: - ```python - def upgrade(): - op.create_table( - 'decisions', - # Identity - sa.Column('decision_id', sa.Text(), nullable=False), - sa.Column('plan_id', sa.Text(), nullable=False), - - # Tree structure - sa.Column('parent_decision_id', sa.Text(), nullable=True), - sa.Column('sequence_number', sa.Integer(), nullable=False), - - # Decision content - sa.Column('decision_type', sa.Text(), nullable=False), - sa.Column('question', sa.Text(), nullable=False), - sa.Column('chosen_option', sa.Text(), nullable=False), - sa.Column('alternatives_considered', sa.JSON(), nullable=False, server_default='[]'), - sa.Column('confidence_score', sa.Float(), nullable=True), - sa.Column('rationale', sa.Text(), nullable=False, server_default=''), - sa.Column('actor_reasoning', sa.Text(), nullable=True), - - # Context - sa.Column('context_snapshot_id', sa.Text(), nullable=False), - sa.Column('checkpoint_id', sa.Text(), nullable=True), - - # Downstream relationships (denormalized for query performance) - sa.Column('downstream_decision_ids', sa.JSON(), nullable=False, server_default='[]'), - sa.Column('downstream_plan_ids', sa.JSON(), nullable=False, server_default='[]'), - sa.Column('artifacts_produced', sa.JSON(), nullable=False, server_default='[]'), - - # Correction tracking - sa.Column('is_correction', sa.Boolean(), nullable=False, server_default='false'), - sa.Column('corrects_decision_id', sa.Text(), nullable=True), - sa.Column('superseded_by', sa.Text(), nullable=True), - - # Timestamp - sa.Column('created_at', sa.Text(), nullable=False), - - # Constraints - sa.PrimaryKeyConstraint('decision_id'), - sa.ForeignKeyConstraint(['plan_id'], ['lifecycle_plans.plan_id'], ondelete='CASCADE'), - sa.ForeignKeyConstraint(['parent_decision_id'], ['decisions.decision_id'], ondelete='SET NULL'), - sa.ForeignKeyConstraint(['context_snapshot_id'], ['context_snapshots.snapshot_id']), - ) - ``` - - [ ] Commit: "feat(db): add decisions table schema" - - [ ] **D5.1c** [Hamza] Add indices for query optimization: - ```python - # Indices for common queries - op.create_index('ix_decisions_plan_id', 'decisions', ['plan_id']) - op.create_index('ix_decisions_parent_id', 'decisions', ['parent_decision_id']) - op.create_index('ix_decisions_plan_sequence', 'decisions', ['plan_id', 'sequence_number']) - op.create_index('ix_decisions_type', 'decisions', ['decision_type']) - op.create_index('ix_decisions_superseded', 'decisions', ['superseded_by'], - postgresql_where=sa.text('superseded_by IS NOT NULL')) - ``` - - [ ] Commit: "feat(db): add decisions table indices" - - [ ] **D5.1d** [Hamza] Add downgrade: - ```python - def downgrade(): - op.drop_index('ix_decisions_superseded') - op.drop_index('ix_decisions_type') - op.drop_index('ix_decisions_plan_sequence') - op.drop_index('ix_decisions_parent_id') - op.drop_index('ix_decisions_plan_id') - op.drop_table('decisions') - ``` - - [ ] Commit: "feat(db): add decisions table downgrade" - - [ ] **D5.2** [Hamza] Create Alembic migration for `decision_dependencies` table: - - [ ] **D5.2a** [Hamza] Define schema for DAG relationships: - ```python - def upgrade(): - op.create_table( - 'decision_dependencies', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('upstream_decision_id', sa.Text(), nullable=False), - sa.Column('downstream_decision_id', sa.Text(), nullable=False), - sa.Column('dependency_type', sa.Text(), nullable=False), # 'data', 'ordering', 'spawned' - sa.Column('created_at', sa.Text(), nullable=False), - - sa.PrimaryKeyConstraint('id'), - sa.ForeignKeyConstraint(['upstream_decision_id'], ['decisions.decision_id'], ondelete='CASCADE'), - sa.ForeignKeyConstraint(['downstream_decision_id'], ['decisions.decision_id'], ondelete='CASCADE'), - sa.UniqueConstraint('upstream_decision_id', 'downstream_decision_id', name='uq_decision_dependency') - ) - op.create_index('ix_dep_upstream', 'decision_dependencies', ['upstream_decision_id']) - op.create_index('ix_dep_downstream', 'decision_dependencies', ['downstream_decision_id']) - ``` - - [ ] Commit: "feat(db): add decision_dependencies table" - - [ ] **D5.2b** [Hamza] Add downgrade: - - [ ] Drop indices and table - - [ ] Commit: "feat(db): add decision_dependencies downgrade" - - [ ] **D5.3** [Hamza] Create Alembic migration for `correction_attempts` table: - - [ ] **D5.3a** [Hamza] Define schema: - ```python - def upgrade(): - op.create_table( - 'correction_attempts', - sa.Column('attempt_id', sa.Text(), nullable=False), - sa.Column('plan_id', sa.Text(), nullable=False), - sa.Column('original_decision_id', sa.Text(), nullable=False), - sa.Column('new_decision_id', sa.Text(), nullable=True), # Set when complete - sa.Column('mode', sa.Text(), nullable=False), # 'revert' or 'append' - sa.Column('guidance', sa.Text(), nullable=False), - sa.Column('status', sa.Text(), nullable=False), # pending, completed, failed - sa.Column('error_message', sa.Text(), nullable=True), - sa.Column('affected_decisions', sa.JSON(), nullable=False, server_default='[]'), - sa.Column('affected_plans', sa.JSON(), nullable=False, server_default='[]'), - sa.Column('created_at', sa.Text(), nullable=False), - sa.Column('completed_at', sa.Text(), nullable=True), - - sa.PrimaryKeyConstraint('attempt_id'), - sa.ForeignKeyConstraint(['plan_id'], ['lifecycle_plans.plan_id'], ondelete='CASCADE'), - sa.ForeignKeyConstraint(['original_decision_id'], ['decisions.decision_id']), - sa.ForeignKeyConstraint(['new_decision_id'], ['decisions.decision_id']) - ) - op.create_index('ix_correction_plan', 'correction_attempts', ['plan_id']) - op.create_index('ix_correction_status', 'correction_attempts', ['status']) - ``` - - [ ] Commit: "feat(db): add correction_attempts table" - - [ ] **D5.3b** [Hamza] Add downgrade: - - [ ] Commit: "feat(db): add correction_attempts downgrade" - - [ ] **D5.4** [Hamza] Create Alembic migration for `context_snapshots` table: - - [ ] **D5.4a** [Hamza] Define schema: - ```python - def upgrade(): - op.create_table( - 'context_snapshots', - sa.Column('snapshot_id', sa.Text(), nullable=False), - sa.Column('hot_context_hash', sa.Text(), nullable=False), - sa.Column('hot_context_ref', sa.Text(), nullable=False), # File path or blob ID - sa.Column('relevant_resources', sa.JSON(), nullable=False, server_default='[]'), - sa.Column('actor_state_ref', sa.Text(), nullable=True), - sa.Column('file_versions', sa.JSON(), nullable=False, server_default='{}'), - sa.Column('content_size_bytes', sa.Integer(), nullable=False, server_default='0'), - sa.Column('created_at', sa.Text(), nullable=False), - - sa.PrimaryKeyConstraint('snapshot_id') - ) - # Index for content deduplication - op.create_index('ix_snapshot_hash', 'context_snapshots', ['hot_context_hash']) - ``` - - [ ] Commit: "feat(db): add context_snapshots table" - - [ ] **D5.4b** [Hamza] Add downgrade: - - [ ] Commit: "feat(db): add context_snapshots downgrade" - - [ ] **D5.5** [Hamza] Create `DecisionModel` in `src/cleveragents/infrastructure/database/models.py`: - - [ ] **D5.5a** [Hamza] Define SQLAlchemy model: - ```python - class DecisionModel(Base): - __tablename__ = 'decisions' - - decision_id = Column(Text, primary_key=True) - plan_id = Column(Text, ForeignKey('lifecycle_plans.plan_id', ondelete='CASCADE'), nullable=False) - parent_decision_id = Column(Text, ForeignKey('decisions.decision_id', ondelete='SET NULL'), nullable=True) - sequence_number = Column(Integer, nullable=False) - - decision_type = Column(Text, nullable=False) - question = Column(Text, nullable=False) - chosen_option = Column(Text, nullable=False) - alternatives_considered = Column(JSON, nullable=False, default=list) - confidence_score = Column(Float, nullable=True) - rationale = Column(Text, nullable=False, default='') - actor_reasoning = Column(Text, nullable=True) - - context_snapshot_id = Column(Text, ForeignKey('context_snapshots.snapshot_id'), nullable=False) - checkpoint_id = Column(Text, nullable=True) - - downstream_decision_ids = Column(JSON, nullable=False, default=list) - downstream_plan_ids = Column(JSON, nullable=False, default=list) - artifacts_produced = Column(JSON, nullable=False, default=list) - - is_correction = Column(Boolean, nullable=False, default=False) - corrects_decision_id = Column(Text, nullable=True) - superseded_by = Column(Text, nullable=True) - - created_at = Column(Text, nullable=False) - - # Relationships - plan = relationship("LifecyclePlanModel", back_populates="decisions") - parent = relationship("DecisionModel", remote_side=[decision_id], backref="children") - context_snapshot = relationship("ContextSnapshotModel") - ``` - - [ ] Commit: "feat(db): add DecisionModel SQLAlchemy class" - - [ ] **D5.5b** [Hamza] Add domain conversion methods: - ```python - def to_domain(self) -> Decision: - """Convert to domain model.""" - return Decision( - decision_id=self.decision_id, - plan_id=self.plan_id, - parent_decision_id=self.parent_decision_id, - sequence_number=self.sequence_number, - decision_type=DecisionType(self.decision_type), - question=self.question, - chosen_option=self.chosen_option, - alternatives_considered=self.alternatives_considered or [], - confidence_score=self.confidence_score, - rationale=self.rationale, - actor_reasoning=self.actor_reasoning, - context_snapshot=self.context_snapshot.to_domain(), - checkpoint_id=self.checkpoint_id, - downstream_decision_ids=self.downstream_decision_ids or [], - downstream_plan_ids=self.downstream_plan_ids or [], - artifacts_produced=self.artifacts_produced or [], - is_correction=self.is_correction, - corrects_decision_id=self.corrects_decision_id, - superseded_by=self.superseded_by, - created_at=datetime.fromisoformat(self.created_at) - ) - - @classmethod - def from_domain(cls, decision: Decision) -> "DecisionModel": - """Create from domain model.""" - return cls( - decision_id=decision.decision_id, - plan_id=decision.plan_id, - parent_decision_id=decision.parent_decision_id, - sequence_number=decision.sequence_number, - decision_type=decision.decision_type.value, - question=decision.question, - chosen_option=decision.chosen_option, - alternatives_considered=decision.alternatives_considered, - confidence_score=decision.confidence_score, - rationale=decision.rationale, - actor_reasoning=decision.actor_reasoning, - context_snapshot_id=decision.context_snapshot.snapshot_id, - checkpoint_id=decision.checkpoint_id, - downstream_decision_ids=decision.downstream_decision_ids, - downstream_plan_ids=decision.downstream_plan_ids, - artifacts_produced=decision.artifacts_produced, - is_correction=decision.is_correction, - corrects_decision_id=decision.corrects_decision_id, - superseded_by=decision.superseded_by, - created_at=decision.created_at.isoformat() - ) - ``` - - [ ] Commit: "feat(db): add DecisionModel conversion methods" - - [ ] **D5.6** [Hamza] Implement `DecisionRepository` in `src/cleveragents/infrastructure/database/repositories.py`: - - [ ] **D5.6a** [Hamza] Define repository class: - ```python - class DecisionRepository: - """Repository for Decision persistence.""" - - def __init__(self, session_factory: Callable[[], Session]): - self._session_factory = session_factory - ``` - - [ ] Commit: "feat(repo): add DecisionRepository scaffold" - - [ ] **D5.6b** [Hamza] Implement `create()`: - ```python - def create(self, decision: Decision) -> Decision: - """Persist a new decision.""" - with self._session_factory() as session: - model = DecisionModel.from_domain(decision) - session.add(model) - try: - session.commit() - except IntegrityError as e: - session.rollback() - if "FOREIGN KEY" in str(e): - raise PlanNotFoundError(decision.plan_id) - raise - return decision - ``` - - [ ] Commit: "feat(repo): implement DecisionRepository.create()" - - [ ] **D5.6c** [Hamza] Implement `get_by_id()`: - ```python - def get_by_id(self, decision_id: str) -> Decision | None: - """Get decision by ID.""" - with self._session_factory() as session: - model = session.query(DecisionModel).options( - joinedload(DecisionModel.context_snapshot) - ).filter_by(decision_id=decision_id).first() - return model.to_domain() if model else None - ``` - - [ ] Commit: "feat(repo): implement DecisionRepository.get_by_id()" - - [ ] **D5.6d** [Hamza] Implement `get_by_plan()`: - ```python - def get_by_plan(self, plan_id: str) -> list[Decision]: - """Get all decisions for a plan, ordered by sequence.""" - with self._session_factory() as session: - models = session.query(DecisionModel).options( - joinedload(DecisionModel.context_snapshot) - ).filter_by(plan_id=plan_id).order_by( - DecisionModel.sequence_number - ).all() - return [m.to_domain() for m in models] - ``` - - [ ] Commit: "feat(repo): implement DecisionRepository.get_by_plan()" - - [ ] **D5.6e** [Hamza] Implement `get_children()`: - ```python - def get_children(self, decision_id: str) -> list[Decision]: - """Get direct children of a decision.""" - with self._session_factory() as session: - models = session.query(DecisionModel).options( - joinedload(DecisionModel.context_snapshot) - ).filter_by(parent_decision_id=decision_id).order_by( - DecisionModel.sequence_number - ).all() - return [m.to_domain() for m in models] - ``` - - [ ] Commit: "feat(repo): implement DecisionRepository.get_children()" - - [ ] **D5.6f** [Hamza] Implement `get_tree()` with recursive CTE: - ```python - def get_tree(self, plan_id: str) -> list[Decision]: - """Get full decision tree for a plan using recursive CTE.""" - with self._session_factory() as session: - # Use recursive CTE for efficient tree retrieval - cte = session.query(DecisionModel).filter( - DecisionModel.plan_id == plan_id, - DecisionModel.parent_decision_id.is_(None) - ).cte(name='decision_tree', recursive=True) - - cte_alias = aliased(DecisionModel, cte) - recursive = session.query(DecisionModel).join( - cte_alias, DecisionModel.parent_decision_id == cte_alias.decision_id - ) - cte = cte.union_all(recursive) - - models = session.query(DecisionModel).select_from(cte).order_by( - DecisionModel.sequence_number - ).all() - return [m.to_domain() for m in models] - ``` - - [ ] Commit: "feat(repo): implement DecisionRepository.get_tree()" - - [ ] **D5.6g** [Hamza] Implement `get_downstream()`: - ```python - def get_downstream(self, decision_id: str) -> list[Decision]: - """Get all downstream decisions (recursive).""" - with self._session_factory() as session: - # Get the starting decision - start = session.query(DecisionModel).filter_by( - decision_id=decision_id - ).first() - if not start: - return [] - - # Recursively collect all downstream - result = [] - to_process = list(start.downstream_decision_ids) - seen = set() - - while to_process: - did = to_process.pop(0) - if did in seen: - continue - seen.add(did) - - d = session.query(DecisionModel).filter_by(decision_id=did).first() - if d: - result.append(d.to_domain()) - to_process.extend(d.downstream_decision_ids) - - return result - ``` - - [ ] Commit: "feat(repo): implement DecisionRepository.get_downstream()" - - [ ] **D5.6h** [Hamza] Implement `update()`: - ```python - def update(self, decision: Decision) -> Decision: - """Update an existing decision.""" - with self._session_factory() as session: - model = session.query(DecisionModel).filter_by( - decision_id=decision.decision_id - ).first() - if not model: - raise DecisionNotFoundError(decision.decision_id) - - # Update fields - model.downstream_decision_ids = decision.downstream_decision_ids - model.downstream_plan_ids = decision.downstream_plan_ids - model.artifacts_produced = decision.artifacts_produced - model.superseded_by = decision.superseded_by - - session.commit() - return decision - ``` - - [ ] Commit: "feat(repo): implement DecisionRepository.update()" - - [ ] **D5.6i** [Hamza] Implement `get_max_sequence()`: - ```python - def get_max_sequence(self, plan_id: str) -> int | None: - """Get maximum sequence number for a plan.""" - with self._session_factory() as session: - result = session.query(func.max(DecisionModel.sequence_number)).filter_by( - plan_id=plan_id - ).scalar() - return result - ``` - - [ ] Commit: "feat(repo): implement DecisionRepository.get_max_sequence()" - - [ ] **D5.6j** [Hamza] Add retry decorator to all methods: - - [ ] Same pattern as other repositories - - [ ] Commit: "feat(repo): add retry decorator to DecisionRepository" - - [ ] Tests: Integration tests for decision persistence - - [ ] **D5.7** [Rui] Write Behave scenarios in `features/decision_persistence.feature`: - - [ ] **D5.7a** [Rui] Basic persistence scenarios: - - [ ] Scenario: Decision persists with all fields - - [ ] Given a valid Decision domain object - - [ ] When I call decision_repo.create(decision) - - [ ] Then decision is stored in database - - [ ] And get_by_id returns the decision - - [ ] And all fields match original - - [ ] Scenario: Decision FK to plan enforced - - [ ] Given no plan with ID "nonexistent" - - [ ] When I try to create decision with that plan_id - - [ ] Then PlanNotFoundError is raised - - [ ] Commit: "test(behave): add basic decision persistence scenarios" - - [ ] **D5.7b** [Rui] Tree query scenarios: - - [ ] Scenario: get_by_plan returns decisions in sequence order - - [ ] Given plan with decisions at sequences 0, 1, 2 - - [ ] When I call get_by_plan(plan_id) - - [ ] Then decisions are returned in sequence order - - [ ] Scenario: get_tree returns full hierarchy - - [ ] Given plan with 3-level decision tree - - [ ] When I call get_tree(plan_id) - - [ ] Then all decisions are returned - - [ ] And tree structure is preserved - - [ ] Scenario: get_children returns only direct children - - [ ] Given D1 -> D2 -> D3 hierarchy - - [ ] When I call get_children(D1.id) - - [ ] Then only D2 is returned - - [ ] Commit: "test(behave): add decision tree query scenarios" - - [ ] **D5.7c** [Rui] Context snapshot scenarios: - - [ ] Scenario: Context snapshot stored and retrievable - - [ ] Given decision with context_snapshot - - [ ] When decision is persisted - - [ ] Then context_snapshot_id is stored - - [ ] And snapshot can be retrieved by ID - - [ ] Scenario: Snapshot deduplication by hash - - [ ] Given two decisions with identical context content - - [ ] Then only one snapshot is stored - - [ ] And both decisions reference same snapshot - - [ ] Commit: "test(behave): add context snapshot persistence scenarios" - - [ ] **D5.7d** [Rui] Correction tracking scenarios: - - [ ] Scenario: Correction attempt persists - - [ ] Given correction attempt with all fields - - [ ] When persisted via CorrectionAttemptRepository - - [ ] Then can be retrieved by attempt_id - - [ ] And status can be updated - - [ ] Scenario: superseded_by updates correctly - - [ ] Given decision D1 - - [ ] When mark_superseded(D1.id, D2.id) called - - [ ] Then D1.superseded_by equals D2.id - - [ ] Commit: "test(behave): add correction persistence scenarios" +**Parallel Group D5: Decision Persistence [Hamza + Luis]** (depends on D1) +- [ ] **COMMIT (Owner: Hamza | Group: D5.db) - Commit message: "feat(db): add decision tables"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Add Alembic migrations for `decisions` and `context_snapshots` with indexes. + - [ ] Code [Hamza]: Add indexes for plan_id, decision_type, and superseded flags for fast tree queries. + - [ ] Docs [Hamza]: Update `docs/reference/database_schema.md` with decision tables. + - [ ] Tests (Behave) [Rui]: Add migration verification scenarios. + - [ ] Tests (Robot) [Rui]: Add DB migration smoke test. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_migration_bench.py` for migration baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(db): add decision tables"`. +- [ ] **COMMIT (Owner: Hamza | Group: D5.repo) - Commit message: "feat(repo): add decision repositories"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement DecisionRepository + ContextSnapshotRepository with tree queries and max-sequence helpers. + - [ ] Code [Hamza]: Add repository methods for superseded decision lookup and subtree retrieval. + - [ ] Docs [Hamza]: Document repository interfaces in `docs/reference/repositories.md`. + - [ ] Tests (Behave) [Rui]: Add decision persistence scenarios (create/query/superseded). + - [ ] Tests (Robot) [Rui]: Add repository integration smoke test. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_repository_bench.py` for tree query performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(repo): add decision repositories"`. +- [ ] **COMMIT (Owner: Luis | Group: D5.di) - Commit message: "feat(di): wire decision services"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Wire decision repositories + services into DI and CLI. + - [ ] Docs [Luis]: Update DI docs for decision wiring. + - [ ] Tests (Behave) [Rui]: Add DI wiring scenarios for decision commands. + - [ ] Tests (Robot) [Rui]: Add CLI smoke test using persisted decisions. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_di_bench.py` for DI resolution overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(di): wire decision services"`. +- [ ] **COMMIT (Owner: Rui | Group: D5.tests) - Commit message: "test(persistence): add decision persistence suites"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Tests (Behave) [Rui]: Add `features/decision_persistence.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add `robot/decision_persistence.robot` E2E coverage. + - [ ] Docs [Rui]: Update `docs/development/testing.md` with decision suites. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_persistence_bench.py` for DB persistence throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "test(persistence): add decision persistence suites"`. -**M4 SUCCESS CRITERIA** (Day 21): -- [ ] Decisions are recorded during Strategize phase with full context -- [ ] Decision tree can be viewed via `agents [--data-dir PATH] [--config-path PATH] plan tree` command -- [ ] `agents [--data-dir PATH] [--config-path PATH] plan explain ` shows full decision details -- [ ] Correction with `--mode=revert` rolls back and re-executes from decision point -- [ ] Correction with `--mode=append` creates fix subplan without modifying history -- [ ] Decisions persist to database and survive restart -- [ ] Context snapshots stored and retrievable for replay +**Parallel Group DOD: Definition of Done + Invariants [Luis + Jeff]** (depends on D2/D4) +- [ ] **COMMIT (Owner: Luis | Group: DOD.dod) - Commit message: "feat(dod): enforce definition-of-done gating"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Evaluate `definition_of_done` before apply; block apply with clear error if unmet. + - [ ] Code [Luis]: Ensure DoD templating uses plan arguments and preserves template in plan metadata. + - [ ] Docs [Luis]: Add `docs/reference/definition_of_done.md` with examples. + - [ ] Tests (Behave) [Rui]: Add DoD pass/fail scenarios. + - [ ] Tests (Robot) [Rui]: Add DoD integration smoke test. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/dod_evaluation_bench.py` for evaluation overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(dod): enforce definition-of-done gating"`. +- [ ] **COMMIT (Owner: Jeff | Group: DOD.invariants) - Commit message: "feat(invariant): add invariant models and enforcement"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Add invariant models, merge order (plan > project > action > global), and enforcement before strategize. + - [ ] Code [Jeff]: Add Invariant Reconciliation Actor role and record `invariant_enforced` decisions. + - [ ] Code [Jeff]: Add `agents invariant add/list/remove` CLI with scope flags. + - [ ] Docs [Jeff]: Add `docs/reference/invariants.md` and update CLI reference. + - [ ] Tests (Behave) [Rui]: Add invariant merge + violation scenarios. + - [ ] Tests (Robot) [Rui]: Add invariant CLI integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/invariant_merge_bench.py` for merge overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(invariant): add invariant models and enforcement"`. --- @@ -6596,1224 +3711,74 @@ TESTING [Rui - Continuous]: Write tests BEFORE implementation **Target: Milestone M5 (+25 days)** -**CRITICAL FOR 30-DAY GOAL**: Subplans enable large project handling (e.g., converting Firefox to Rust uses hierarchical decomposition into thousands of subplans) +**Parallel Group E1: Subplan Domain [Luis + Rui]** +- [ ] **COMMIT (Owner: Luis | Group: E1.domain) - Commit message: "feat(domain): add subplan config and status models"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Add `ExecutionMode`, `MergeStrategy`, `SubplanConfig`, `SubplanStatus`, and `SubplanAttempt` models. + - [ ] Code [Luis]: Extend `Plan` with parent/root IDs, subplan statuses, and helpers (`is_subplan`, `has_subplans`). + - [ ] Docs [Luis]: Add `docs/reference/subplan_model.md`. + - [ ] Tests (Behave) [Rui]: Add `features/subplan_model.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add `robot/subplan_model.robot` smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_model_bench.py` for model validation. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(domain): add subplan config and status models"`. -- [ ] **Stage E1: Subplan Model** (Day 12) **[Luis]** - - **SEQUENTIAL ORDER**: E1.1 (Enums) → E1.2 (SubplanConfig) → E1.3 (Plan extension) → E1.4 (SubplanStatus) → E1.5 (Failure rules) → E1.6 (Tests) - - - [ ] **E1.1** [Luis] Define execution enums in `src/cleveragents/domain/models/core/plan.py`: - - [ ] **E1.1a** [Luis] Define `ExecutionMode` enum: - ```python - class ExecutionMode(str, Enum): - """How subplans should be executed.""" - SEQUENTIAL = "sequential" # One after another, ordered by sequence - PARALLEL = "parallel" # All at once (up to max_parallel) - DEPENDENCY_ORDERED = "dependency_ordered" # Respect DAG dependencies - ``` - - [ ] Commit: "feat(domain): define ExecutionMode enum" - - [ ] **E1.1b** [Luis] Define `MergeStrategy` enum: - ```python - class MergeStrategy(str, Enum): - """How to merge results from parallel subplans.""" - GIT_THREE_WAY = "git_three_way" # Use git merge-file for code - SEQUENTIAL_APPLY = "sequential_apply" # Apply in completion order - FAIL_ON_CONFLICT = "fail_on_conflict" # Error if any conflicts - LAST_WINS = "last_wins" # Later changes overwrite earlier - ``` - - [ ] Commit: "feat(domain): define MergeStrategy enum" - - [ ] **E1.2** [Luis] Define `SubplanConfig` model: - - [ ] **E1.2a** [Luis] Create SubplanConfig dataclass: - ```python - class SubplanConfig(BaseModel): - """Configuration for subplan execution.""" - - execution_mode: ExecutionMode = Field( - default=ExecutionMode.SEQUENTIAL, - description="How to execute subplans" - ) - merge_strategy: MergeStrategy = Field( - default=MergeStrategy.GIT_THREE_WAY, - description="How to merge subplan results" - ) - max_parallel: int = Field( - default=5, ge=1, le=50, - description="Max concurrent subplans (for PARALLEL mode)" - ) - fail_fast: bool = Field( - default=False, - description="Stop all subplans on first failure" - ) - timeout_per_subplan_seconds: int | None = Field( - default=None, - description="Timeout for each subplan (None=no timeout)" - ) - retry_failed: bool = Field( - default=True, - description="Automatically retry failed subplans" - ) - max_retries: int = Field( - default=2, ge=0, le=5, - description="Max retry attempts per subplan" - ) - ``` - - [ ] Commit: "feat(domain): define SubplanConfig model" - - [ ] **E1.3** [Luis] Extend Plan model for subplan hierarchy: - - [ ] **E1.3a** [Luis] Add parent/root plan fields (verify exist): - ```python - # In Plan model - parent_plan_id: str | None = Field( - default=None, - description="Parent plan ID if this is a subplan" - ) - root_plan_id: str | None = Field( - default=None, - description="Root plan ID (topmost ancestor)" - ) - ``` - - [ ] Commit: "feat(domain): verify parent/root plan fields on Plan" - - [ ] **E1.3b** [Luis] Add subplan configuration field: - ```python - subplan_config: SubplanConfig | None = Field( - default=None, - description="Config for subplan execution (set on parent plans)" - ) - subplan_statuses: list["SubplanStatus"] = Field( - default_factory=list, - description="Status tracking for spawned subplans" - ) - ``` - - [ ] Commit: "feat(domain): add subplan config and status fields to Plan" - - [ ] **E1.3c** [Luis] Add computed properties: - ```python - @property - def is_subplan(self) -> bool: - """Check if this plan is a subplan (has parent).""" - return self.parent_plan_id is not None - - @property - def is_root_plan(self) -> bool: - """Check if this is the root plan.""" - return self.root_plan_id is None or self.root_plan_id == self.plan_id - - @property - def depth(self) -> int: - """Distance from root plan (0 for root).""" - # Note: This requires parent chain traversal - # For efficiency, may be cached or stored - if self.is_root_plan: - return 0 - # Computed by service layer traversing parent_plan_id chain - return -1 # Placeholder, computed externally - - @property - def has_subplans(self) -> bool: - """Check if this plan has spawned subplans.""" - return len(self.subplan_statuses) > 0 - ``` - - [ ] Commit: "feat(domain): add subplan computed properties to Plan" - - [ ] **E1.4** [Luis] Define `SubplanStatus` tracking model: - - [ ] **E1.4a** [Luis] Create SubplanStatus dataclass: - ```python - @dataclass - class SubplanStatus: - """Track status of a spawned subplan.""" - - subplan_id: str # The subplan's plan_id - action_name: str # Action used to create subplan - target_resources: list[str] # Resources subplan works on - - # Status tracking - status: ProcessingState = ProcessingState.QUEUED - started_at: datetime | None = None - completed_at: datetime | None = None - - # Results - error: str | None = None - changeset_summary: str | None = None # Brief summary of changes - files_changed: int = 0 - - # Retries - attempt_number: int = 1 - previous_attempts: list["SubplanAttempt"] = field(default_factory=list) - ``` - - [ ] Commit: "feat(domain): define SubplanStatus dataclass" - - [ ] **E1.4b** [Luis] Define SubplanAttempt for retry tracking: - ```python - @dataclass - class SubplanAttempt: - """Record of a subplan execution attempt.""" - attempt_number: int - started_at: datetime - completed_at: datetime | None - error: str | None - was_retried: bool - ``` - - [ ] Commit: "feat(domain): define SubplanAttempt dataclass" - - [ ] **E1.5** [Luis] Define subplan failure handling rules: - - [ ] **E1.5a** [Luis] Create `SubplanFailureHandler` class: - ```python - class SubplanFailureHandler: - """Handle subplan failures based on configuration.""" - - def should_stop_others( - self, - config: SubplanConfig, - failed_status: SubplanStatus - ) -> bool: - """Determine if other subplans should stop.""" - if config.fail_fast: - return True - if config.execution_mode == ExecutionMode.SEQUENTIAL: - return True # Sequential always stops on failure - return False # Parallel continues others - - def should_retry( - self, - config: SubplanConfig, - status: SubplanStatus - ) -> bool: - """Determine if failed subplan should be retried.""" - if not config.retry_failed: - return False - if status.attempt_number > config.max_retries: - return False - # Don't retry on certain errors - if status.error and "ValidationError" in status.error: - return True # Validation failures can be retried - if status.error and "TimeoutError" in status.error: - return True # Timeouts can be retried - return False - ``` - - [ ] Commit: "feat(domain): define SubplanFailureHandler" - - [ ] **E1.5b** [Luis] Add failure state constants: - ```python - # Error = application/system bug, likely not recoverable - # Failure = task couldn't complete (tests fail, validation fail), may be retryable - - RETRIABLE_FAILURES = { - "ValidationError", - "TimeoutError", - "TemporaryResourceError", - "MergeConflictError" # May succeed with different merge strategy - } - - NON_RETRIABLE_ERRORS = { - "ConfigurationError", - "AuthenticationError", - "MissingResourceError", - "CircularDependencyError" - } - ``` - - [ ] Commit: "feat(domain): define retriable vs non-retriable failures" - - [ ] **E1.6** [Rui] Write Behave tests for subplan model: - - [ ] **E1.6a** [Rui] Plan hierarchy scenarios: - - [ ] Scenario: Plan with parent_plan_id has is_subplan=True - - [ ] Given Plan with parent_plan_id set - - [ ] Then is_subplan returns True - - [ ] And is_root_plan returns False - - [ ] Scenario: Root plan has is_subplan=False and is_root_plan=True - - [ ] Scenario: SubplanConfig validates max_parallel bounds - - [ ] Commit: "test(behave): add plan hierarchy scenarios" - - [ ] **E1.6b** [Rui] Execution mode scenarios: - - [ ] Scenario: ExecutionMode enum has all required values - - [ ] Scenario: MergeStrategy enum has all required values - - [ ] Scenario: SubplanConfig defaults are applied - - [ ] Commit: "test(behave): add execution mode scenarios" - - [ ] **E1.6c** [Rui] SubplanStatus scenarios: - - [ ] Scenario: SubplanStatus tracks state correctly - - [ ] Scenario: SubplanAttempt records retry history - - [ ] Scenario: Failure handler respects fail_fast setting - - [ ] Commit: "test(behave): add SubplanStatus scenarios" +**Parallel Group E2: Subplan Spawning [Jeff + Aditya]** (depends on D2 + E1) +- [ ] **COMMIT (Owner: Jeff | Group: E2.service) - Commit message: "feat(service): add subplan service and spawn workflow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Implement `SubplanService` with `spawn_subplan`, `spawn_batch`, tree queries, bounded context builder. + - [ ] Code [Jeff]: Link SUBPLAN_SPAWN decisions to created subplans and status tracking. + - [ ] Docs [Jeff]: Add `docs/reference/subplan_service.md`. + - [ ] Tests (Behave) [Rui]: Add subplan spawn scenarios. + - [ ] Tests (Robot) [Rui]: Add subplan spawn integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_spawn_bench.py` for spawn throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(service): add subplan service and spawn workflow"`. +- [ ] **COMMIT (Owner: Aditya | Group: E2.actor) - Commit message: "feat(actor): add plan_subplan tool and decision emission"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Aditya]: Add `plan_subplan` tool to strategy actors and emit SUBPLAN_SPAWN decisions. + - [ ] Docs [Aditya]: Update actor YAML examples for subplan emission. + - [ ] Tests (Behave) [Rui]: Add scenarios for subplan decision emission. + - [ ] Tests (Robot) [Rui]: Add actor tool integration smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_actor_tool_bench.py` for tool invocation overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Aditya]: `git commit -m "feat(actor): add plan_subplan tool and decision emission"`. -- [ ] **Stage E2: Subplan Spawning** (Day 12-13) **[Jeff + Luis]** - - **SEQUENTIAL ORDER**: E2.1 (Service scaffold) → E2.2 (spawn_subplan) → E2.3 (spawn_batch) → E2.4 (queries) → E2.5 (strategy actor) → E2.6 (execute phase) → E2.7 (status tracking) → E2.8 (bounded context) → E2.9 (Tests) - - - [ ] **E2.1** [Jeff] Create `SubplanService` scaffold in `src/cleveragents/application/services/subplan_service.py`: - - [ ] **E2.1a** [Jeff] Define service class: - ```python - class SubplanService: - """Service for spawning and managing subplans.""" - - def __init__( - self, - plan_service: PlanLifecycleService, - decision_service: DecisionService, - plan_repo: LifecyclePlanRepository, - context_builder: ContextBuilder - ): - self._plan_service = plan_service - self._decision_service = decision_service - self._plan_repo = plan_repo - self._context_builder = context_builder - ``` - - [ ] Commit: "feat(service): add SubplanService scaffold" - - [ ] **E2.2** [Jeff] Implement `spawn_subplan()` method: - - [ ] **E2.2a** [Jeff] Core implementation: - ```python - def spawn_subplan( - self, - parent_plan: Plan, - decision: Decision, - action_name: str, - target_resources: list[str] | None = None, - arguments: dict | None = None - ) -> Plan: - """Spawn a single subplan from a parent plan.""" - # Validate parent is not already a deep subplan - if parent_plan.depth >= 10: # Max nesting depth - raise MaxSubplanDepthError(f"Cannot spawn subplan at depth {parent_plan.depth + 1}") - - # Create subplan via plan service - subplan = self._plan_service.use_action( - action_name=action_name, - project_ids=target_resources or parent_plan.project_ids, - arguments=arguments or {}, - parent_plan_id=parent_plan.plan_id, - root_plan_id=parent_plan.root_plan_id or parent_plan.plan_id, - automation_level=parent_plan.automation_level - ) - - # Link to decision - self._decision_service.add_downstream_plan(decision.decision_id, subplan.plan_id) - - # Create status tracking - status = SubplanStatus( - subplan_id=subplan.plan_id, - action_name=action_name, - target_resources=target_resources or [] - ) - - # Update parent with new subplan status - self._update_parent_subplan_status(parent_plan.plan_id, status) - - logger.info(f"Spawned subplan {subplan.plan_id} from parent {parent_plan.plan_id}") - return subplan - ``` - - [ ] Commit: "feat(service): implement spawn_subplan()" - - [ ] **E2.2b** [Jeff] Add bounded context calculation: - ```python - def _build_bounded_context( - self, - parent_plan: Plan, - decision: Decision, - target_resources: list[str] - ) -> BoundedContext: - """Build bounded context for subplan from decision scope.""" - return self._context_builder.build_from_decision( - parent_context=parent_plan.context, - decision=decision, - resource_filter=target_resources - ) - ``` - - [ ] Commit: "feat(service): add bounded context for subplans" - - [ ] **E2.3** [Jeff] Implement `spawn_batch()` method: - - [ ] **E2.3a** [Jeff] Batch spawning implementation: - ```python - def spawn_batch( - self, - parent_plan: Plan, - decisions: list[Decision], - execution_mode: ExecutionMode = ExecutionMode.PARALLEL - ) -> list[Plan]: - """Spawn multiple subplans at once.""" - subplans = [] - - for decision in decisions: - if decision.decision_type != DecisionType.SUBPLAN_SPAWN: - continue - - # Extract spawn parameters from decision - spawn_params = self._extract_spawn_params(decision) - - subplan = self.spawn_subplan( - parent_plan=parent_plan, - decision=decision, - action_name=spawn_params["action_name"], - target_resources=spawn_params.get("target_resources"), - arguments=spawn_params.get("arguments") - ) - subplans.append(subplan) - - # Update parent's execution mode - self._update_parent_execution_mode(parent_plan.plan_id, execution_mode) - - logger.info(f"Spawned {len(subplans)} subplans from parent {parent_plan.plan_id}") - return subplans - - def _extract_spawn_params(self, decision: Decision) -> dict: - """Extract spawn parameters from SUBPLAN_SPAWN decision.""" - # Parse chosen_option which contains action name and params - # Format: "action_name:arg1=val1,arg2=val2" - return { - "action_name": decision.chosen_option.split(":")[0], - "target_resources": decision.context_snapshot.relevant_resources, - "arguments": {} # Parsed from decision metadata - } - ``` - - [ ] Commit: "feat(service): implement spawn_batch()" - - [ ] **E2.4** [Jeff] Implement query methods: - - [ ] **E2.4a** [Jeff] Implement `get_subplans()`: - ```python - def get_subplans(self, parent_plan_id: str) -> list[Plan]: - """Get all direct child subplans.""" - return self._plan_repo.get_children(parent_plan_id) - - def get_subplan_statuses(self, parent_plan_id: str) -> list[SubplanStatus]: - """Get status tracking for all subplans.""" - parent = self._plan_repo.get_by_id(parent_plan_id) - return parent.subplan_statuses if parent else [] - ``` - - [ ] Commit: "feat(service): implement get_subplans()" - - [ ] **E2.4b** [Jeff] Implement `get_full_tree()`: - ```python - def get_full_tree(self, root_plan_id: str) -> PlanTree: - """Get full subplan tree from root.""" - plans = self._plan_repo.get_tree(root_plan_id) - return self._build_plan_tree(plans, root_plan_id) - - def _build_plan_tree(self, plans: list[Plan], root_id: str) -> PlanTree: - """Build tree structure from flat list.""" - by_parent: dict[str, list[Plan]] = {} - root = None - - for plan in plans: - if plan.plan_id == root_id: - root = plan - elif plan.parent_plan_id: - by_parent.setdefault(plan.parent_plan_id, []).append(plan) - - def build_node(plan: Plan) -> PlanTreeNode: - children = [build_node(c) for c in by_parent.get(plan.plan_id, [])] - return PlanTreeNode(plan=plan, children=children) - - return PlanTree(root=build_node(root), total_count=len(plans)) - ``` - - [ ] Commit: "feat(service): implement get_full_tree()" - - [ ] **E2.5** [Aditya] Configure strategy actor to emit subplan_spawn decisions: - - [ ] **E2.5a** [Aditya] Add plan_subplan tool to strategy actor: - ```yaml - # In strategy_actor.yaml - tools: - - name: plan_subplan - description: | - Decompose work into a subplan for parallel or sequential execution. - Use when work can be broken into independent pieces. - parameters: - - name: action_name - type: string - description: Action to use for subplan (e.g., "local/code-fix") - - name: description - type: string - description: What the subplan should accomplish - - name: target_files - type: array - description: Files this subplan should work on - - name: execution_mode - type: string - enum: [sequential, parallel, dependency_ordered] - description: How this relates to other subplans - - name: depends_on - type: array - description: IDs of subplans this depends on (for dependency_ordered) - code: | - result = context.create_subplan_decision( - action_name=input_data["action_name"], - description=input_data["description"], - target_files=input_data.get("target_files", []), - execution_mode=input_data.get("execution_mode", "parallel"), - depends_on=input_data.get("depends_on", []) - ) - ``` - - [ ] Commit: "feat(actor): add plan_subplan tool to strategy actor" - - [ ] **E2.5b** [Aditya] Implement `context.create_subplan_decision()`: - ```python - def create_subplan_decision( - self, - action_name: str, - description: str, - target_files: list[str], - execution_mode: str = "parallel", - depends_on: list[str] | None = None - ) -> str: - """Create a SUBPLAN_SPAWN decision (not actual plan yet).""" - decision = self._decision_service.record_decision( - plan_id=self.plan_id, - decision_type=DecisionType.SUBPLAN_SPAWN, - question=f"Should we create subplan for: {description}", - chosen_option=f"{action_name}:{','.join(target_files)}", - hot_context=self._get_context_for_files(target_files), - resources=target_files, - parent_decision_id=self._current_decision_id, - rationale=description - ) - - # Store metadata for Execute phase to process - self._pending_subplans.append({ - "decision_id": decision.decision_id, - "action_name": action_name, - "target_files": target_files, - "execution_mode": execution_mode, - "depends_on": depends_on or [] - }) - - return decision.decision_id - ``` - - [ ] Commit: "feat(context): implement create_subplan_decision()" - - [ ] **E2.6** [Jeff] Execute phase processes subplan decisions: - - [ ] **E2.6a** [Jeff] Add subplan processing to execute phase: - ```python - # In PlanLifecycleService.execute_execution() - - async def _process_subplan_decisions(self, plan: Plan) -> None: - """Process SUBPLAN_SPAWN decisions after strategy.""" - # Get all pending subplan decisions - decisions = self._decision_service.get_by_plan_and_type( - plan.plan_id, DecisionType.SUBPLAN_SPAWN - ) - - if not decisions: - return - - # Group by execution mode - parallel_decisions = [] - sequential_decisions = [] - dependency_decisions = [] - - for d in decisions: - mode = self._get_execution_mode(d) - if mode == ExecutionMode.PARALLEL: - parallel_decisions.append(d) - elif mode == ExecutionMode.SEQUENTIAL: - sequential_decisions.append(d) - else: - dependency_decisions.append(d) - - # Execute in appropriate order - if parallel_decisions: - await self._execute_parallel_subplans(plan, parallel_decisions) - if sequential_decisions: - await self._execute_sequential_subplans(plan, sequential_decisions) - if dependency_decisions: - await self._execute_dependency_ordered_subplans(plan, dependency_decisions) - ``` - - [ ] Commit: "feat(service): add subplan decision processing to execute phase" - - [ ] **E2.6b** [Jeff] Implement sequential execution: - ```python - async def _execute_sequential_subplans( - self, - parent: Plan, - decisions: list[Decision] - ) -> None: - """Execute subplans one at a time in order.""" - for decision in decisions: - subplan = self._subplan_service.spawn_subplan( - parent_plan=parent, - decision=decision, - action_name=self._extract_action_name(decision) - ) - - # Execute and wait - await self._execute_subplan(subplan) - - # Check result - status = self._get_subplan_status(parent, subplan.plan_id) - if status.status == ProcessingState.ERRORED: - if parent.subplan_config.fail_fast: - raise SubplanFailedError(subplan.plan_id, status.error) - # Otherwise continue to next - ``` - - [ ] Commit: "feat(service): implement sequential subplan execution" - - [ ] **E2.7** [Luis] Implement subplan status tracking: - - [ ] **E2.7a** [Luis] Create status update mechanism: - ```python - class SubplanStatusTracker: - """Track and update subplan statuses.""" - - def __init__(self, plan_repo: LifecyclePlanRepository): - self._plan_repo = plan_repo - self._listeners: dict[str, list[Callable]] = {} - - def update_status( - self, - parent_plan_id: str, - subplan_id: str, - new_status: ProcessingState, - error: str | None = None - ) -> None: - """Update status of a subplan.""" - parent = self._plan_repo.get_by_id(parent_plan_id) - if not parent: - return - - # Find and update status - for status in parent.subplan_statuses: - if status.subplan_id == subplan_id: - status.status = new_status - if new_status == ProcessingState.PROCESSING: - status.started_at = datetime.utcnow() - elif new_status in (ProcessingState.COMPLETE, ProcessingState.ERRORED): - status.completed_at = datetime.utcnow() - if error: - status.error = error - break - - # Persist - self._plan_repo.update(parent) - - # Notify listeners - self._notify_listeners(parent_plan_id, subplan_id, new_status) - - def subscribe(self, parent_plan_id: str, callback: Callable) -> None: - """Subscribe to status updates for a parent plan.""" - self._listeners.setdefault(parent_plan_id, []).append(callback) - ``` - - [ ] Commit: "feat(service): implement SubplanStatusTracker" - - [ ] **E2.7b** [Luis] Determine parent state from subplan states: - ```python - def compute_parent_state(self, parent: Plan) -> ProcessingState: - """Compute parent state based on subplan states.""" - statuses = parent.subplan_statuses - - if not statuses: - return parent.state - - # Count states - errored = sum(1 for s in statuses if s.status == ProcessingState.ERRORED) - complete = sum(1 for s in statuses if s.status == ProcessingState.COMPLETE) - processing = sum(1 for s in statuses if s.status == ProcessingState.PROCESSING) - - config = parent.subplan_config or SubplanConfig() - - # Determine parent state - if processing > 0: - return ProcessingState.PROCESSING - - if errored > 0: - if config.execution_mode == ExecutionMode.PARALLEL: - # Parallel: error only if ALL failed - if errored == len(statuses): - return ProcessingState.ERRORED - else: - # Sequential: error on first failure - return ProcessingState.ERRORED - - if complete == len(statuses): - return ProcessingState.COMPLETE - - return ProcessingState.QUEUED # Some still pending - ``` - - [ ] Commit: "feat(service): implement parent state computation" - - [ ] **E2.8** [Luis] Implement bounded context for subplans: - - [ ] **E2.8a** [Luis] Create `ContextBuilder` for bounded contexts: - ```python - class ContextBuilder: - """Build bounded contexts for subplans.""" - - def build_from_decision( - self, - parent_context: PlanContext, - decision: Decision, - resource_filter: list[str] - ) -> BoundedContext: - """Build context bounded to decision scope.""" - # Filter files to only those relevant - relevant_files = self._filter_files( - parent_context.files, - resource_filter - ) - - # Include decision chain for reference - decision_chain = self._get_decision_chain(decision) - - return BoundedContext( - files=relevant_files, - decision_chain=decision_chain, - parent_context_ref=parent_context.context_id, - boundary=resource_filter - ) - - def _filter_files( - self, - files: dict[str, FileContent], - patterns: list[str] - ) -> dict[str, FileContent]: - """Filter files to match patterns.""" - import fnmatch - result = {} - for path, content in files.items(): - if any(fnmatch.fnmatch(path, p) for p in patterns): - result[path] = content - return result - ``` - - [ ] Commit: "feat(context): implement ContextBuilder for bounded contexts" - - [ ] **E2.9** [Rui] Write integration tests for subplan spawning: - - [ ] **E2.9a** [Rui] Spawn scenarios: - - [ ] Scenario: SUBPLAN_SPAWN decision creates child plan - - [ ] Given strategy produces SUBPLAN_SPAWN decision - - [ ] When execute phase processes decisions - - [ ] Then child plan is created with correct parent_plan_id - - [ ] And decision.downstream_plan_ids contains subplan ID - - [ ] Scenario: spawn_batch creates multiple subplans - - [ ] Given 3 SUBPLAN_SPAWN decisions - - [ ] When spawn_batch is called - - [ ] Then 3 subplans are created - - [ ] Commit: "test(behave): add subplan spawn scenarios" - - [ ] **E2.9b** [Rui] Execution order scenarios: - - [ ] Scenario: Sequential subplans execute in order - - [ ] Given 3 sequential subplans S1, S2, S3 - - [ ] When executed - - [ ] Then S1 completes before S2 starts - - [ ] And S2 completes before S3 starts - - [ ] Scenario: Parallel subplans execute concurrently - - [ ] Given 3 parallel subplans - - [ ] When executed with max_parallel=3 - - [ ] Then all 3 start at approximately same time - - [ ] Commit: "test(behave): add execution order scenarios" - - [ ] **E2.9c** [Rui] Failure scenarios: - - [ ] Scenario: Failed sequential subplan stops processing - - [ ] Given sequential subplans S1, S2, S3 - - [ ] When S2 fails - - [ ] Then S3 is not started - - [ ] And parent enters ERRORED state - - [ ] Scenario: Failed parallel subplan allows others to finish - - [ ] Given parallel subplans S1, S2, S3 - - [ ] And fail_fast=False - - [ ] When S2 fails - - [ ] Then S1 and S3 continue to completion - - [ ] Commit: "test(behave): add subplan failure scenarios" +**Parallel Group E3: Parallel Execution [Luis + Jeff]** (depends on E1/E2) +- [ ] **COMMIT (Owner: Luis | Group: E3.exec) - Commit message: "feat(service): add subplan scheduler and execution"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Add subplan scheduler with `max_parallel`, dependency ordering, and fail-fast handling. + - [ ] Code [Luis]: Track status updates for subplans and propagate to parent plan. + - [ ] Docs [Luis]: Add `docs/reference/subplan_execution.md`. + - [ ] Tests (Behave) [Rui]: Add parallel + dependency execution scenarios. + - [ ] Tests (Robot) [Rui]: Add parallel execution integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_scheduler_bench.py` for scheduler overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(service): add subplan scheduler and execution"`. -- [ ] **Stage E3: Parallel Execution** (Day 19) **[Jeff + Luis]** - - **SEQUENTIAL ORDER**: E3.1 (AsyncExecutor) → E3.2 (Semaphore) → E3.3 (Timeouts) → E3.4 (DAG) → E3.5 (Isolation) → E3.6 (Tests) - - - [ ] **E3.1** [Jeff] Implement async subplan executor: - - [ ] **E3.1a** [Jeff] Create `AsyncSubplanExecutor` class: - ```python - class AsyncSubplanExecutor: - """Execute subplans asynchronously with concurrency control.""" - - def __init__( - self, - plan_service: PlanLifecycleService, - status_tracker: SubplanStatusTracker - ): - self._plan_service = plan_service - self._status_tracker = status_tracker - - async def execute_parallel( - self, - parent: Plan, - subplans: list[Plan], - config: SubplanConfig - ) -> list[SubplanResult]: - """Execute subplans in parallel with concurrency limit.""" - semaphore = asyncio.Semaphore(config.max_parallel) - - async def execute_with_limit(subplan: Plan) -> SubplanResult: - async with semaphore: - return await self._execute_single(subplan, config) - - tasks = [execute_with_limit(sp) for sp in subplans] - results = await asyncio.gather(*tasks, return_exceptions=True) - - return self._process_results(results, subplans) - ``` - - [ ] Commit: "feat(executor): add AsyncSubplanExecutor" - - [ ] **E3.1b** [Jeff] Implement single subplan execution: - ```python - async def _execute_single( - self, - subplan: Plan, - config: SubplanConfig - ) -> SubplanResult: - """Execute a single subplan with timeout.""" - try: - # Apply timeout if configured - if config.timeout_per_subplan_seconds: - result = await asyncio.wait_for( - self._run_subplan(subplan), - timeout=config.timeout_per_subplan_seconds - ) - else: - result = await self._run_subplan(subplan) - - return SubplanResult( - subplan_id=subplan.plan_id, - success=True, - changeset=result.changeset - ) - - except asyncio.TimeoutError: - self._status_tracker.update_status( - subplan.parent_plan_id, - subplan.plan_id, - ProcessingState.ERRORED, - error="Timeout exceeded" - ) - return SubplanResult( - subplan_id=subplan.plan_id, - success=False, - error="TimeoutError" - ) - - except Exception as e: - self._status_tracker.update_status( - subplan.parent_plan_id, - subplan.plan_id, - ProcessingState.ERRORED, - error=str(e) - ) - return SubplanResult( - subplan_id=subplan.plan_id, - success=False, - error=str(e) - ) - ``` - - [ ] Commit: "feat(executor): implement single subplan execution with timeout" - - [ ] **E3.2** [Jeff] Implement dependency-ordered execution: - - [ ] **E3.2a** [Jeff] Build and validate dependency DAG: - ```python - def build_dependency_dag( - self, - decisions: list[Decision] - ) -> DependencyGraph: - """Build DAG from subplan decisions with depends_on.""" - graph = DependencyGraph() - - for decision in decisions: - graph.add_node(decision.decision_id) - depends_on = self._get_depends_on(decision) - for dep_id in depends_on: - graph.add_edge(dep_id, decision.decision_id) - - # Validate no cycles - if graph.has_cycle(): - cycle = graph.find_cycle() - raise CircularDependencyError(f"Cycle detected: {' -> '.join(cycle)}") - - return graph - ``` - - [ ] Commit: "feat(executor): implement dependency DAG building" - - [ ] **E3.2b** [Jeff] Execute in topological order: - ```python - async def execute_dependency_ordered( - self, - parent: Plan, - decisions: list[Decision], - config: SubplanConfig - ) -> list[SubplanResult]: - """Execute subplans respecting dependency order.""" - dag = self.build_dependency_dag(decisions) - execution_order = dag.topological_sort() - - results = [] - completed: set[str] = set() - - # Process in waves - each wave contains independent nodes - while execution_order: - # Find all nodes whose dependencies are satisfied - ready = [ - node for node in execution_order - if all(dep in completed for dep in dag.get_dependencies(node)) - ] - - if not ready: - break # Stuck - shouldn't happen with valid DAG - - # Execute ready nodes in parallel - ready_decisions = [d for d in decisions if d.decision_id in ready] - subplans = [self._spawn_subplan(parent, d) for d in ready_decisions] - - wave_results = await self.execute_parallel(parent, subplans, config) - results.extend(wave_results) - - # Mark completed - for r in wave_results: - if r.success: - completed.add(r.decision_id) - - # Remove from order - execution_order = [n for n in execution_order if n not in ready] - - return results - ``` - - [ ] Commit: "feat(executor): implement dependency-ordered execution" - - [ ] **E3.3** [Luis] Implement subplan isolation: - - [ ] **E3.3a** [Luis] Ensure separate sandboxes: - ```python - def ensure_isolated_sandbox( - self, - subplan: Plan, - resource_service: ResourceService - ) -> None: - """Ensure subplan has its own isolated sandbox.""" - for resource_id in subplan.project_ids: - resource = self._get_resource(resource_id) - # Each subplan gets unique sandbox for same resource - sandbox = resource_service.access_resource( - plan_id=subplan.plan_id, # Use subplan ID, not parent - resource=resource, - mode=AccessMode.WRITE - ) - # Sandbox is isolated by plan_id - ``` - - [ ] Commit: "feat(executor): ensure isolated sandboxes for subplans" - - [ ] **E3.3b** [Luis] Prevent cross-subplan visibility: - ```python - def validate_isolation( - self, - subplan: Plan, - other_subplans: list[Plan] - ) -> None: - """Verify subplan cannot access other subplans' state.""" - subplan_sandbox = self._get_sandbox(subplan.plan_id) - - for other in other_subplans: - if other.plan_id == subplan.plan_id: - continue - other_sandbox = self._get_sandbox(other.plan_id) - - # Verify different paths - if subplan_sandbox.sandbox_path == other_sandbox.sandbox_path: - raise IsolationViolationError( - f"Subplans {subplan.plan_id} and {other.plan_id} share sandbox" - ) - ``` - - [ ] Commit: "feat(executor): add isolation validation" - - [ ] **E3.4** [Rui] Write tests for parallel execution: - - [ ] **E3.4a** [Rui] Concurrency scenarios: - - [ ] Scenario: 10 independent subplans run with max_parallel=5 - - [ ] Given 10 subplans with no dependencies - - [ ] And max_parallel=5 - - [ ] When executed in parallel - - [ ] Then at most 5 run concurrently at any time - - [ ] And all 10 complete successfully - - [ ] Commit: "test(behave): add concurrency limit scenarios" - - [ ] **E3.4b** [Rui] Dependency scenarios: - - [ ] Scenario: Dependency chain executes in correct order - - [ ] Given subplans A -> B -> C (B depends on A, C depends on B) - - [ ] When executed with dependency ordering - - [ ] Then A completes before B starts - - [ ] And B completes before C starts - - [ ] Scenario: Diamond dependency executes correctly - - [ ] Given A -> B, A -> C, B -> D, C -> D - - [ ] When executed - - [ ] Then A runs first - - [ ] Then B and C run in parallel - - [ ] Then D runs last - - [ ] Commit: "test(behave): add dependency ordering scenarios" - - [ ] **E3.4c** [Rui] Timeout scenarios: - - [ ] Scenario: Subplan timeout triggers failure - - [ ] Given subplan with timeout_per_subplan_seconds=10 - - [ ] When subplan takes 15 seconds - - [ ] Then subplan is marked ERRORED with TimeoutError - - [ ] Commit: "test(behave): add timeout scenarios" +**Parallel Group E4: Result Merging [Jeff + Luis]** (depends on E3) +- [ ] **COMMIT (Owner: Jeff | Group: E4.merge) - Commit message: "feat(merge): add subplan merge strategies"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Add three-way merge strategy for file changes and conflict markers. + - [ ] Code [Luis]: Add sequential merge and JSON merge strategies; expose merge result artifacts. + - [ ] Docs [Jeff]: Add `docs/reference/subplan_merge.md`. + - [ ] Tests (Behave) [Rui]: Add merge + conflict scenarios. + - [ ] Tests (Robot) [Rui]: Add merge integration tests for multi-subplan plans. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_merge_bench.py` for merge performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(merge): add subplan merge strategies"`. -- [ ] **Stage E4: Result Merging** (Day 20) **[Jeff + Luis]** - - **SEQUENTIAL ORDER**: E4.1 (MergeService) → E4.2 (MergeResult) → E4.3 (ThreeWayMerge) → E4.4 (SequentialMerge) → E4.5 (Validation) → E4.6 (Tests) - - - [ ] **E4.1** [Jeff] Create `MergeService` in `src/cleveragents/application/services/merge_service.py`: - - [ ] **E4.1a** [Jeff] Define service class: - ```python - class MergeService: - """Service for merging subplan results.""" - - def __init__( - self, - sandbox_manager: SandboxManager, - validation_service: ValidationService - ): - self._sandbox_manager = sandbox_manager - self._validation_service = validation_service - ``` - - [ ] Commit: "feat(service): add MergeService scaffold" - - [ ] **E4.1b** [Jeff] Implement `merge_subplan_results()`: - ```python - def merge_subplan_results( - self, - parent: Plan, - subplans: list[Plan], - strategy: MergeStrategy = MergeStrategy.GIT_THREE_WAY - ) -> MergeResult: - """Merge changesets from all completed subplans.""" - # Collect changesets - changesets = [sp.changeset for sp in subplans if sp.changeset] - - # Group changes by file - changes_by_file: dict[str, list[Change]] = {} - for cs in changesets: - for change in cs.changes: - changes_by_file.setdefault(change.path, []).append(change) - - # Detect and handle conflicts - merged_changes = [] - conflicts = [] - - for path, changes in changes_by_file.items(): - if len(changes) == 1: - merged_changes.append(changes[0]) - else: - # Multiple subplans modified same file - result = self._merge_file_changes(path, changes, strategy) - if result.has_conflict: - conflicts.append(result) - merged_changes.append(result.merged_change) - - return MergeResult( - merged_changeset=ChangeSet(changes=merged_changes), - conflicts=conflicts, - source_subplan_ids=[sp.plan_id for sp in subplans] - ) - ``` - - [ ] Commit: "feat(service): implement merge_subplan_results()" - - [ ] **E4.2** [Jeff] Define merge result types: - - [ ] **E4.2a** [Jeff] Define MergeResult dataclass: - ```python - @dataclass - class MergeResult: - """Result of merging subplan changesets.""" - merged_changeset: ChangeSet - conflicts: list["FileConflict"] - source_subplan_ids: list[str] - - @property - def has_conflicts(self) -> bool: - return len(self.conflicts) > 0 - - @property - def conflict_count(self) -> int: - return len(self.conflicts) - - @dataclass - class FileConflict: - """Conflict in a single file.""" - path: str - conflict_regions: list["ConflictRegion"] - subplan_ids: list[str] # Which subplans caused conflict - merged_content_with_markers: str - - @dataclass - class ConflictRegion: - """Region of conflict within a file.""" - start_line: int - end_line: int - ours_content: str # From first subplan - theirs_content: str # From second subplan - ``` - - [ ] Commit: "feat(domain): define merge result types" - - [ ] **E4.3** [Jeff] Implement git-style three-way merge: - - [ ] **E4.3a** [Jeff] Implement `_merge_file_changes()`: - ```python - def _merge_file_changes( - self, - path: str, - changes: list[Change], - strategy: MergeStrategy - ) -> FileMergeResult: - """Merge multiple changes to same file.""" - if strategy == MergeStrategy.GIT_THREE_WAY: - return self._git_three_way_merge(path, changes) - elif strategy == MergeStrategy.SEQUENTIAL_APPLY: - return self._sequential_merge(path, changes) - elif strategy == MergeStrategy.LAST_WINS: - return FileMergeResult( - merged_change=changes[-1], - has_conflict=False - ) - else: - raise ValueError(f"Unknown strategy: {strategy}") - ``` - - [ ] Commit: "feat(service): implement merge strategy dispatch" - - [ ] **E4.3b** [Jeff] Implement `_git_three_way_merge()`: - ```python - def _git_three_way_merge( - self, - path: str, - changes: list[Change] - ) -> FileMergeResult: - """Perform git-style three-way merge.""" - import subprocess - import tempfile - - # Get base (original) content - base_content = self._get_base_content(path) - - # For now, handle 2 changes; extend for more - if len(changes) != 2: - # Fall back to sequential for >2 changes - return self._sequential_merge(path, changes) - - ours = changes[0].content or base_content - theirs = changes[1].content or base_content - - # Write to temp files - with tempfile.NamedTemporaryFile(mode='w', suffix='.base', delete=False) as f: - f.write(base_content) - base_path = f.name - with tempfile.NamedTemporaryFile(mode='w', suffix='.ours', delete=False) as f: - f.write(ours) - ours_path = f.name - with tempfile.NamedTemporaryFile(mode='w', suffix='.theirs', delete=False) as f: - f.write(theirs) - theirs_path = f.name - - try: - # Run git merge-file - result = subprocess.run( - ['git', 'merge-file', '-p', ours_path, base_path, theirs_path], - capture_output=True, - text=True - ) - - merged_content = result.stdout - has_conflict = result.returncode != 0 - - # Parse conflict markers if present - conflicts = [] - if has_conflict: - conflicts = self._parse_conflict_markers(merged_content) - - merged_change = Change( - path=path, - operation=OperationType.MODIFY, - content=merged_content - ) - - return FileMergeResult( - merged_change=merged_change, - has_conflict=has_conflict, - conflict_regions=conflicts - ) - finally: - # Cleanup temp files - for p in [base_path, ours_path, theirs_path]: - os.unlink(p) - ``` - - [ ] Commit: "feat(service): implement git three-way merge" - - [ ] **E4.4** [Luis] Implement sequential merge: - - [ ] **E4.4a** [Luis] Apply changes in order: - ```python - def _sequential_merge( - self, - path: str, - changes: list[Change] - ) -> FileMergeResult: - """Apply changes sequentially in completion order.""" - current_content = self._get_base_content(path) - - for change in changes: - if change.edits: - # Apply edits - current_content = self._apply_edits(current_content, change.edits) - elif change.content: - # Full replacement - current_content = change.content - - return FileMergeResult( - merged_change=Change( - path=path, - operation=OperationType.MODIFY, - content=current_content - ), - has_conflict=False - ) - ``` - - [ ] Commit: "feat(service): implement sequential merge" - - [ ] **E4.5** [Luis] Implement post-merge validation: - - [ ] **E4.5a** [Luis] Validate merged state: - ```python - async def validate_merged_result( - self, - parent: Plan, - merge_result: MergeResult - ) -> ValidationResult: - """Run validation on merged changes.""" - # Apply merged changes to temporary sandbox - temp_sandbox = self._sandbox_manager.create_temp_sandbox( - parent.plan_id, suffix="_merge_validation" - ) - - try: - # Apply merged changes - for change in merge_result.merged_changeset.changes: - self._apply_change_to_sandbox(temp_sandbox, change) - - # Run validation - result = await self._validation_service.validate_changeset( - merge_result.merged_changeset, - parent.project - ) - - if not result.passed: - logger.warning( - f"Post-merge validation failed: {result.errors}" - ) - - return result - finally: - temp_sandbox.cleanup() - ``` - - [ ] Commit: "feat(service): implement post-merge validation" - - [ ] **E4.5b** [Luis] Handle validation failures: - ```python - async def handle_validation_failure( - self, - parent: Plan, - merge_result: MergeResult, - validation_result: ValidationResult - ) -> MergeRecoveryAction: - """Determine recovery action for failed validation.""" - # Options: - # 1. Retry with different merge strategy - # 2. Escalate to user - # 3. Fall back to sequential execution - - if parent.subplan_config.merge_strategy == MergeStrategy.GIT_THREE_WAY: - # Try sequential as fallback - return MergeRecoveryAction.RETRY_SEQUENTIAL - - # Escalate to user - return MergeRecoveryAction.ESCALATE_TO_USER - ``` - - [ ] Commit: "feat(service): implement validation failure handling" - - [ ] **E4.6** [Rui] Write tests for result merging: - - [ ] **E4.6a** [Rui] Clean merge scenarios: - - [ ] Scenario: Two subplans modifying different files merge cleanly - - [ ] Given subplan A modifies file1.py - - [ ] And subplan B modifies file2.py - - [ ] When merged - - [ ] Then both changes are in merged_changeset - - [ ] And has_conflicts is False - - [ ] Scenario: Same file different lines merges cleanly - - [ ] Given subplan A changes line 10 of file.py - - [ ] And subplan B changes line 50 of file.py - - [ ] When merged with GIT_THREE_WAY - - [ ] Then both changes are preserved - - [ ] And has_conflicts is False - - [ ] Commit: "test(behave): add clean merge scenarios" - - [ ] **E4.6b** [Rui] Conflict scenarios: - - [ ] Scenario: Same lines creates conflict markers - - [ ] Given subplan A changes line 10 to "version A" - - [ ] And subplan B changes line 10 to "version B" - - [ ] When merged with GIT_THREE_WAY - - [ ] Then has_conflicts is True - - [ ] And merged content contains conflict markers - - [ ] Scenario: LAST_WINS strategy has no conflicts - - [ ] Given conflicting changes - - [ ] When merged with LAST_WINS - - [ ] Then has_conflicts is False - - [ ] And later change overwrites earlier - - [ ] Commit: "test(behave): add conflict merge scenarios" - - [ ] **E4.6c** [Rui] Validation scenarios: - - [ ] Scenario: Post-merge validation catches broken code - - [ ] Given merged code with syntax error - - [ ] When post-merge validation runs - - [ ] Then validation fails - - [ ] And recovery action is suggested - - [ ] Commit: "test(behave): add post-merge validation scenarios" +**Parallel Group E5: Multi-Project Plans [Hamza + Luis]** (depends on E2/E4) +- [ ] **COMMIT (Owner: Hamza | Group: E5.multi) - Commit message: "feat(plan): add multi-project subplan support"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Allow plans to target multiple projects with separate resource link contexts. + - [ ] Code [Luis]: Ensure sandbox isolation and cross-project dependency resolution. + - [ ] Docs [Hamza]: Add `docs/reference/multi_project_plans.md`. + - [ ] Tests (Behave) [Rui]: Add multi-project subplan scenarios. + - [ ] Tests (Robot) [Rui]: Add multi-project integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/multi_project_bench.py` for multi-project overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(plan): add multi-project subplan support"`. - [ ] **Stage E5: Multi-Project Plans** (Day 25) **[Hamza]** @@ -8006,81 +3971,73 @@ TESTING [Rui - Continuous]: Write tests BEFORE implementation --- -### Section 8: Server Connectivity [DEFERRED - Beyond Day 30] +**Parallel Group G1: Large-Project Decomposition [Jeff + Luis]** +- [ ] **COMMIT (Owner: Jeff | Group: G1.decompose) - Commit message: "feat(plan): add large-project decomposition and dependency closure"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Add hierarchical decomposition with 4+ levels and bounded context per subplan. + - [ ] Code [Luis]: Add dependency closure computation for large graphs and DAG execution ordering. + - [ ] Docs [Jeff]: Add `docs/reference/large_project_decomposition.md`. + - [ ] Tests (Behave) [Rui]: Add deep hierarchy + dependency closure scenarios. + - [ ] Tests (Robot) [Rui]: Add large-project decomposition integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/large_project_decompose_bench.py` for decomposition runtime. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(plan): add large-project decomposition and dependency closure"`. -**Target: Post-30-day work (NOT part of initial 30-day timeline)** +**Parallel Group G2: Checkpointing & Rollback [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: G2.checkpoint) - Commit message: "feat(checkpoint): add checkpointing and rollback"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Add checkpoint declarations for tools and plan-level rollback policy. + - [ ] Code [Luis]: Implement `plan rollback ` command. + - [ ] Docs [Luis]: Add `docs/reference/checkpointing.md`. + - [ ] Tests (Behave) [Rui]: Add checkpoint/rollback scenarios. + - [ ] Tests (Robot) [Rui]: Add rollback integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/checkpoint_rollback_bench.py` for rollback latency. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(checkpoint): add checkpointing and rollback"`. -> **IMPORTANT**: This section covers **client-side interfaces for connecting to an external server**. The server itself is a **separate project** that will be developed independently. This client will NOT include server functionality—it operates purely as a client that can either run in stand-alone local-only mode or connect to a separately deployed CleverAgents server. +**Parallel Group G3: Semantic Validation [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: G3.semantic) - Commit message: "feat(validation): add semantic validation service"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Add semantic validation hooks during strategize/execute and error-pattern checks. + - [ ] Docs [Luis]: Add `docs/reference/semantic_validation.md`. + - [ ] Tests (Behave) [Rui]: Add semantic validation scenarios. + - [ ] Tests (Robot) [Rui]: Add semantic validation integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/semantic_validation_bench.py` for validation cost. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(validation): add semantic validation service"`. -#### Stage F0: Server Client Interface Stubs [Day 28-29 - REQUIRED DURING MVP] +**Parallel Group G4: Context Tiers & Views [Hamza + Rui]** +- [ ] **COMMIT (Owner: Hamza | Group: G4.context) - Commit message: "feat(context): add hot/warm/cold tiers and actor views"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement hot/warm/cold tiers with indexing, LRU eviction, and promotion/demotion. + - [ ] Code [Hamza]: Add per-actor context views (strategist/executor/reviewer) and filtered presentation. + - [ ] Docs [Hamza]: Add `docs/reference/context_tiers.md`. + - [ ] Tests (Behave) [Rui]: Add context tier scenarios. + - [ ] Tests (Robot) [Rui]: Add context tier integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/context_tiers_bench.py` for tier lookup performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(context): add hot/warm/cold tiers and actor views"`. -These stubs ensure the client architecture supports future server connectivity without implementing it: +**Parallel Group G5: Cost & Risk Estimation [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: G5.estimate) - Commit message: "feat(estimation): add cost and risk estimation actor"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Add optional `estimation_actor` role and cost/risk estimation outputs. + - [ ] Docs [Hamza]: Add `docs/reference/estimation.md` with output format. + - [ ] Tests (Behave) [Rui]: Add estimation scenarios. + - [ ] Tests (Robot) [Rui]: Add estimation integration smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/estimation_actor_bench.py` for estimation runtime. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(estimation): add cost and risk estimation actor"`. -- [ ] **Stage F0: Server Client Interface Stubs** (Day 28-29) **[Luis - Required]** - - [ ] **F0.1** [Luis] Create `src/cleveragents/interfaces/server_client.py` with protocol stubs: - - [ ] `class ServerClient(Protocol):` with all method signatures for client-to-server communication - - [ ] `async def connect(server_url: str) -> None: raise NotImplementedError("Server connectivity not yet implemented")` - - [ ] `async def disconnect() -> None: raise NotImplementedError(...)` - - [ ] `async def sync_action(action: Action) -> Action: raise NotImplementedError(...)` - - [ ] `async def request_remote_execution(plan_id: str) -> str: raise NotImplementedError(...)` - - [ ] Commit: "feat(interfaces): add ServerClient protocol stub" - - [ ] **F0.2** [Luis] Create `src/cleveragents/interfaces/remote_execution_client.py`: - - [ ] `class RemoteExecutionClient(Protocol):` - protocol for requesting remote plan execution from server - - [ ] `async def submit_plan(plan_id: str, server_url: str) -> str: raise NotImplementedError(...)` - - [ ] `async def poll_status(execution_id: str) -> ExecutionStatus: raise NotImplementedError(...)` - - [ ] `async def fetch_results(execution_id: str) -> ExecutionResult: raise NotImplementedError(...)` - - [ ] Commit: "feat(interfaces): add RemoteExecutionClient protocol stub" - - [ ] **F0.3** [Luis] Create `src/cleveragents/interfaces/auth_client.py`: - - [ ] `class AuthClient(Protocol):` - protocol for client authentication with server - - [ ] `async def authenticate(credentials: Credentials) -> AuthToken: raise NotImplementedError(...)` - - [ ] `async def validate_token(token: str) -> TokenValidation: raise NotImplementedError(...)` - - [ ] `async def refresh_token(token: str) -> AuthToken: raise NotImplementedError(...)` - - [ ] Commit: "feat(interfaces): add AuthClient protocol stub" - - [ ] **F0.4** [Luis] Add `agents [--data-dir PATH] [--config-path PATH] connect` CLI command as stub: - - [ ] Add to `src/cleveragents/cli/commands/server_client.py` - - [ ] Command signature: `@click.command("connect") @click.argument("server_url")` - - [ ] Implementation: `click.echo("Server connectivity not yet implemented. Coming soon!")`; `raise SystemExit(1)` - - [ ] Commit: "feat(cli): add connect command stub" - - [ ] **F0.5** [Rui] Write minimal tests verifying stubs raise NotImplementedError: - - [ ] Test: Calling ServerClient methods raises NotImplementedError - - [ ] Test: `agents [--data-dir PATH] [--config-path PATH] connect` displays "not implemented" message and exits - - [ ] Commit: "test(behave): add server client stub tests" - ---- - -#### Stages F1-F4: Server Client Implementation [DEFERRED - Beyond Day 30] - -> **These stages are OUT OF SCOPE for the 30-day timeline.** Do not begin work on them until after Day 30 milestone is achieved. Note: These stages implement **client-side** connectivity; the server is a separate project. - -- [ ] **Stage F1: Server Client Infrastructure** (Post-Day 30) **[Luis]** **[DEFERRED]** - - [ ] **F1.1** [Luis] Create HTTP client in `src/cleveragents/infrastructure/server_client.py` - - [ ] **F1.2** [Luis] Connection health check and version negotiation - - [ ] **F1.3** [Luis] API client code generation from server OpenAPI spec - - [ ] **F1.4** [Rui] Client connection tests (with mock server) - -- [ ] **Stage F2: Plan Sync Client** (Post-Day 30) **[Luis]** **[DEFERRED]** - - [ ] **F2.1** [Luis] Sync local actions to server - - [ ] **F2.2** [Luis] Request plan creation on server - - [ ] **F2.3** [Luis] Request plan execution on server - - [ ] **F2.4** [Luis] Request `agents [--data-dir PATH] [--config-path PATH] plan apply` on server - - [ ] **F2.5** [Luis] Fetch plan status from server - - [ ] **F2.6** [Rui] Client-side API integration tests (with mock server) - -- [ ] **Stage F3: WebSocket Client** (Post-Day 30) **[Luis]** **[DEFERRED]** - - [ ] **F3.1** [Luis] WebSocket client for receiving plan updates from server - - [ ] **F3.2** [Luis] Handle phase transitions, node completions from server - - [ ] **F3.3** [Rui] WebSocket client tests (with mock server) - -- [ ] **Stage F4: Remote Project Support** (Post-Day 30) **[Hamza]** **[DEFERRED]** - - [ ] **F4.1** [Hamza] Client can specify remote resources for server execution - - [ ] **F4.2** [Hamza] Client sends execution requests to server for remote resources - - [ ] **F4.3** [Rui] End-to-end tests for remote execution (with mock server) - -**M7 SUCCESS CRITERIA** (Post-Day 30): -- [ ] `agents [--data-dir PATH] [--config-path PATH] connect ` establishes connection to an external server -- [ ] Plans can be synced and executed on a remote server -- [ ] Real-time updates received via WebSocket from server -- [ ] Remote projects can be specified and executed on server +**Parallel Group G6: CLI Polish [All]** +- [ ] **COMMIT (Owner: Jeff | Group: G6.cli) - Commit message: "chore(cli): polish help and output"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [All]: Standardize help text, progress indicators, and error messages with recovery hints. + - [ ] Docs [All]: Update CLI output examples where needed. + - [ ] Tests (Robot) [Rui]: Add CLI UX smoke tests for critical commands. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/cli_render_bench.py` for output rendering overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "chore(cli): polish help and output"`. **--- MERGE POINT 2: Day 30 - Large Project Autonomy Target (LOCAL MODE ONLY) ---** @@ -8099,47 +4056,17 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is **Target: Milestone M7 (+35 days)** -- [ ] **Stage G1: Automation Levels Enhancement** (Day 31-32) **[Luis]** - - [ ] **G1.1** [Luis] Full manual mode with decision prompts: - - [ ] Every decision point pauses for human input - - [ ] Display context, alternatives considered, recommendation - - [ ] Accept explicit choice or custom guidance - - [ ] Record user decisions in decision tree - - [ ] **G1.2** [Luis] Review-before-apply with diff display: - - [ ] AI makes all decisions autonomously during Strategize - - [ ] Execution completes in sandbox - - [ ] Human reviews complete diff before apply: - - [ ] Show changed files summary - - [ ] Show full unified diff with syntax highlighting - - [ ] Show risk warnings (auth code, migrations, etc.) - - [ ] User can approve, reject, or correct specific decisions - - [ ] **G1.3** [Luis] Full automation with confidence escalation: - - [ ] AI makes all decisions autonomously - - [ ] Execution proceeds through apply without pause - - [ ] Human notified of completion - - [ ] Rollback available if issues detected post-apply - - [ ] EVEN in full automation, critical decisions escalate: - - [ ] If confidence below threshold, request human guidance - - [ ] If touching critical files (defined by project), escalate - - [ ] If cost exceeds budget, escalate - - [ ] **G1.4** [Luis] Progressive trust building (track success rates): - - [ ] Track decision success rates per decision type - - [ ] Track codebase familiarity scores per project - - [ ] Confidence increases with successful history - - [ ] Allow automatic upgrade: manual -> review -> full - - [ ] After N successful plans in manual, suggest review mode - - [ ] After N successful plans in review, suggest full mode - - [ ] **G1.5** [Luis] Implement `AutonomyController` class: - - [ ] Method `assess_decision_confidence(decision, context) -> float` - - [ ] Method `should_escalate(decision, confidence, automation_level) -> bool` - - [ ] Method `get_historical_success(decision_type) -> float` - - [ ] Method `get_familiarity_score(project) -> float` - - [ ] **G1.6** [Rui] Tests for each automation level: - - [ ] Scenario: Manual mode pauses at each decision - - [ ] Scenario: Review-before-apply shows diff before apply - - [ ] Scenario: Full automation completes without pause - - [ ] Scenario: Low confidence in full automation escalates - - [ ] Scenario: Progressive trust upgrade suggestion shown +**Parallel Group F0: Server Client Stubs [Luis + Rui]** (required for M6; no server implementation) +- [ ] **COMMIT (Owner: Luis | Group: F0.stubs) - Commit message: "feat(interfaces): add server client stubs"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Add protocol stubs for `ServerClient`, `RemoteExecutionClient`, and `AuthClient` with NotImplementedError. + - [ ] Code [Luis]: Add `agents connect ` CLI stub in `cli/commands/server_client.py`. + - [ ] Docs [Luis]: Add `docs/reference/server_client_stubs.md` noting client-only behavior. + - [ ] Tests (Behave) [Rui]: Add stub behavior scenarios. + - [ ] Tests (Robot) [Rui]: Add CLI stub smoke test. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/server_stub_bench.py` (baseline no-op). + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(interfaces): add server client stubs"`. - [ ] **Stage G2: Checkpointing & Rollback** (Day 32-33) **[Luis]** - [ ] **G2.1** [Luis] Skill-level checkpoint declarations @@ -8262,55 +4189,66 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is **Note**: Quality automation setup is in Section 0; Section 10 focuses on async infrastructure and later-stage validation support. -- [ ] **Stage 10A: Async Infrastructure** (Days 10-12) **[Luis]** - - [ ] Code: Implement async patterns per ADR-002 - - [ ] **10A.1** [Luis] Implement async command execution - - [X] **10A.2** Implement the 33 retry patterns with tenacity (COMPLETED 2025-11-17) - - [X] **10A.3** Add circuit breaker for failures (COMPLETED 2025-11-17) - - [ ] **10A.4** [Luis] Add background workers (convert 7 concurrency patterns to asyncio tasks) - - [ ] **10A.5** [Luis] Integrate retry patterns into new services +**Parallel Group 10A: Async Infrastructure [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: 10A.async) - Commit message: "feat(async): add async command execution and workers"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Implement async command execution per ADR-002 with cancellation and timeout handling. + - [ ] Code [Luis]: Add background worker orchestration for plan lifecycle events. + - [ ] Docs [Luis]: Update `docs/reference/async_architecture.md` with execution flow and shutdown rules. + - [ ] Tests (Behave) [Rui]: Add `features/async_execution.feature` for async command handling. + - [ ] Tests (Robot) [Rui]: Add `robot/async_execution.robot` smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/async_execution_bench.py` for worker scheduling overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(async): add async command execution and workers"`. +- [ ] **COMMIT (Owner: Luis | Group: 10A.retry) - Commit message: "feat(async): wire retry policies into services"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Integrate retry/circuit breaker policies into service layer operations. + - [ ] Docs [Luis]: Document retry policy defaults and override points. + - [ ] Tests (Behave) [Rui]: Add retry/circuit breaker behavior scenarios. + - [ ] Tests (Robot) [Rui]: Add resilience smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/retry_policy_bench.py` for retry overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(async): wire retry policies into services"`. -- [ ] **Stage 10B: Selective Quality Review** (Days 4-8) **[Brent]** - - [ ] Focus Areas: Only review critical items - - [ ] **10B.1** [Brent] Review architectural decisions in PRs: - - [ ] Service layer design choices - - [ ] Database schema decisions - - [ ] API contract definitions - - [ ] Skip: formatting, simple CRUD, test files - - [ ] **10B.2** [Brent] Review complex algorithms: - - [ ] Decision tree traversal logic - - [ ] Merge conflict resolution - - [ ] Dependency closure computation - - [ ] Skip: straightforward implementations - - [ ] **10B.3** [Brent] Review security-sensitive code: - - [ ] Authentication/authorization - - [ ] Input validation - - [ ] Sandbox boundaries - - [ ] Skip: code already scanned by bandit - - [ ] **10B.4** [Brent] Monitor automated quality metrics: - - [ ] Daily check of CI/CD dashboard - - [ ] Weekly quality report generation - - [ ] Escalate only if metrics drop +**Parallel Group 10B: Selective Quality Review [Brent]** +- [ ] **COMMIT (Owner: Brent | Group: 10B.review) - Commit message: "docs(qa): add review playbook and priority matrix"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Docs [Brent]: Create `docs/development/review_playbook.md` with focus areas and skip rules. + - [ ] Docs [Brent]: Add priority matrix and review SLA guidance. + - [ ] Tests (Behave) [Rui]: Add scenarios validating review playbook references exist. + - [ ] Tests (Robot) [Rui]: Add docs build smoke test covering the new guide. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/docs_build_bench.py` for docs build baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Brent]: `git commit -m "docs(qa): add review playbook and priority matrix"`. -- [ ] **Stage 10C: Validation Testing Support** (Days 9-30) **[Brent + Luis]** - - [ ] High-impact testing work - - [ ] **10C.1** [Brent] Create edge case test scenarios: - - [ ] Concurrent plan execution edge cases - - [ ] Resource conflict scenarios - - [ ] Validation failure chains - - [ ] Rollback edge cases - - [ ] **10C.2** [Brent + Luis] Implement semantic validation tests: - - [ ] API compatibility validation - - [ ] Business invariant preservation - - [ ] Cross-resource consistency - - [ ] **10C.3** [Brent] Performance testing for scale: - - [ ] 10K+ file repository handling - - [ ] Memory usage profiling - - [ ] Context tier performance - - [ ] **10C.4** [Brent] Create validation test fixtures: - - [ ] Invalid code samples - - [ ] Edge case project structures - - [ ] Malformed input data +**Parallel Group 10C: Validation Testing Support [Brent + Luis]** +- [ ] **COMMIT (Owner: Brent | Group: 10C.edge) - Commit message: "test(validation): add edge case suites"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Brent]: Add shared edge-case fixtures under `features/fixtures/validation/`. + - [ ] Docs [Brent]: Update `docs/development/testing.md` with validation test catalog. + - [ ] Tests (Behave) [Rui]: Add edge-case scenarios for concurrency, conflicts, and rollbacks. + - [ ] Tests (Robot) [Rui]: Add integration coverage for edge-case suites. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/validation_edge_bench.py` for edge-case runtime. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Brent]: `git commit -m "test(validation): add edge case suites"`. +- [ ] **COMMIT (Owner: Luis | Group: 10C.semantic) - Commit message: "test(validation): add semantic validation suites"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Add semantic validation fixtures and error-pattern samples. + - [ ] Docs [Luis]: Document semantic validation coverage expectations. + - [ ] Tests (Behave) [Rui]: Add semantic validation scenarios. + - [ ] Tests (Robot) [Rui]: Add semantic validation integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/semantic_validation_suite_bench.py` for suite runtime. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "test(validation): add semantic validation suites"`. +- [ ] **COMMIT (Owner: Brent | Group: 10C.performance) - Commit message: "test(perf): add scale test fixtures"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Brent]: Add scale fixtures for 1K/5K/10K file repos in `features/fixtures/scale/`. + - [ ] Docs [Brent]: Add scale test runbook and environment notes. + - [ ] Tests (Behave) [Rui]: Add scale test scenarios validating thresholds. + - [ ] Tests (Robot) [Rui]: Add large-project Robot tests for performance runs. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/scale_fixture_bench.py` for baseline performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Brent]: `git commit -m "test(perf): add scale test fixtures"`. --- -- 2.52.0 From 234ce876822d46dd471893159bec868d1996e056 Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Freeman Date: Wed, 11 Feb 2026 21:04:11 -0500 Subject: [PATCH 05/11] Docs: updated the implementation plan again --- implementation_plan.md | 3780 +++++++++------------------------------- 1 file changed, 801 insertions(+), 2979 deletions(-) diff --git a/implementation_plan.md b/implementation_plan.md index a8d1c17fb..f69aff782 100644 --- a/implementation_plan.md +++ b/implementation_plan.md @@ -537,10 +537,10 @@ The following work from the previous implementation has been completed and will ``` CRITICAL PATH (Sequential): -Day 1: A5 Plan/Action Persistence (Luis) +Day 1: A5 Plan/Action Persistence + Action Arguments (Luis) Day 2: B1.core + B2.service/B3.cli Project/Resource models + CLI (Hamza) Day 3: B4.sandbox Git worktree sandbox (Luis + Hamza) -Day 4: C1.schema/C1.examples + C2.loader/C2.compiler Actor schema + compilation (Aditya + Jeff) +Day 4: C1.schema/C1.examples + C2.legacy + C2.loader/C2.compiler Actor schema + compilation (Aditya + Jeff) Day 5: C3.protocol/C3.context/C3.inline + C4.file Skill framework + file skills (Jeff) Day 6: C4.search/C4.git + C5.model/C5.router/C5.diff Change tracking + tool routing (Luis + Jeff) Day 7: C6.pipeline/C6.gating + C7.mcp + C8.providers + C9.execute/C9.apply Plan-actor integration + validation (Aditya + Jeff + Luis) @@ -687,13 +687,13 @@ Execute all required tests through the appropriate `nox` sessions—never call ` | Day | Task IDs | Description | Blocks | |-----|----------|-------------|--------| -| **Day 1** | A5.1, A5.2, A5.5 | Plan/Action DB schema + Plan Repository | Luis (A5.3-A5.4), All downstream | -| **Day 2** | A5.7, A5.8 | Service integration + DI wiring | CLI integration (A4 tests) | -| **Day 3** | C3.1, C3.2 | Skill Protocol + Metadata | All skill implementations | -| **Day 4** | C3.3 | SkillContext (read/write/spawn) | Aditya (C3.4), Luis (C4) | -| **Day 5** | C3.6a-b | WriteFileSkill, EditFileSkill | Change tracking tests | -| **Day 6** | C3.6f-g | Skill error handling + registry | Actor compilation | -| **Day 7** | C7 | Plan-Actor integration | MVP verification | +| **Day 1** | A5.alpha, A5.action_arguments | Plan/Action DB schema (incl. action args) | Luis (A5.beta), All downstream | +| **Day 2** | A5.gamma | Service integration + DI wiring | CLI integration (A4 tests) | +| **Day 3** | A5.legacy, B3.cleanup, C3.protocol | Remove legacy plan/project CLI + skill protocol | All skill implementations | +| **Day 4** | C3.context, C3.inline, C2.legacy | SkillContext + inline executor + v2 actor cleanup | Luis (C5.model) | +| **Day 5** | C4.file | File operation skills | Change tracking tests | +| **Day 6** | C4.search, C4.git | Search + git skills | Actor compilation (C2.compiler) | +| **Day 7** | C6.gating, C9.execute, C9.apply | Plan-actor integration + validation | MVP verification | | **Day 8** | M1.1-M1.10 | MVP merge point coordination | Release v0.1.0-rc1 | | **Day 15-16** | D4.1 | Correction Service core algorithm | Decision correction | | **Day 17** | D4.2 | Sandbox checkpointing | Re-execution | @@ -808,7 +808,7 @@ B4.sandbox (Strategy + Manager) → B4.sandbox git_worktree → B4.sandbox copy_ **CHAIN 4: Actor Layer (Days 4-7)** ``` -C1.schema (Actor Schema) → C2.loader (Actor Loader) → C2.compiler (Actor Compiler) → C2.refs (Reference Resolution) +C1.schema (Actor Schema) → C2.legacy (Drop v2 configs) → C2.loader (Actor Loader) → C2.compiler (Actor Compiler) → C2.refs (Reference Resolution) ``` **CHAIN 5: Skill Layer (Days 5-8)** @@ -837,12 +837,13 @@ C9.execute (Strategize/Execute) → C6.pipeline/C6.gating (Validation) → C9.ap WEEK 1 PARALLEL TRACKS: TRACK A [Jeff - CRITICAL PATH LEAD + Luis - ARCHITECTURE]: -├── Day 1 AM: Jeff - A5.alpha DB migrations (2-3 hours) +├── Day 1 AM: Jeff - A5.alpha DB migrations + A5.action_arguments (2-3 hours) │ └── Luis can start A5.beta ORM models after schema doc review ├── Day 1 PM: Jeff - A5.gamma repositories (2-3 hours) │ └── Luis - A5.beta ORM models (parallel) ├── Day 2: Jeff - A5.gamma service integration (main blocker) │ └── Luis - A5.gamma DI wiring +├── Day 3: Jeff - A5.legacy plan CLI cleanup + B3.cleanup legacy project CLI ├── Day 3-4: Jeff - C3.protocol/C3.context/C3.inline Skill framework (CRITICAL) │ └── Luis - C5.model Change models + tracker (parallel after C3.protocol) ├── Day 5-6: Jeff - C4.file/C4.search Built-in skills (file/dir/search) @@ -864,6 +865,7 @@ TRACK B [Hamza - INFRASTRUCTURE (No LLM Knowledge Needed)]: TRACK C [Aditya - ACTORS (Domain Expert - Hierarchical Configs)]: ├── Day 4: C1.schema Actor YAML schema models │ └── Aditya writes ALL actor YAML examples (C1.examples) +├── Day 4-5: Jeff - C2.legacy remove v2 actor configs (dependency for C2.loader) ├── Day 5: C2.loader + C2.compiler Actor loading + compilation ├── Day 6: C2.refs Reference resolution + C7.mcp MCP Skill Adapter ├── Day 7: C8.providers Built-in provider actors (openai/, anthropic/, openrouter/) @@ -1135,91 +1137,71 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Test security scanning catches eval() - [ ] Commit: "test(qa): add pre-commit hook tests" -- [ ] **Stage Q1: CI/CD Pipeline** (Day 2) **[Brent]** - - [ ] Code: Create GitHub Actions workflow for automated PR validation - - [ ] **Q1.1** [Brent] Create `.github/workflows/pr-validation.yml`: - - [ ] **Q1.1a** Set up Python 3.13 with pip caching - - [ ] **Q1.1b** Install all dependencies including dev/test - - [ ] **Q1.1c** Run pre-commit on all files - - [ ] Commit: "feat(ci): add PR validation workflow" - - [ ] **Q1.2** [Brent] Add automated checks with GitHub annotations: - - [ ] **Q1.2a** Run ruff with GitHub output format - - [ ] **Q1.2b** Parse pyright JSON output to GitHub annotations - - [ ] **Q1.2c** Parse bandit results to security annotations - - [ ] Commit: "feat(ci): add linting and type checking" - - [ ] **Q1.3** [Brent] Add test execution with coverage: - - [ ] **Q1.3a** Run `nox -s unit_tests` with XML output - - [ ] **Q1.3b** Run `nox -s coverage_report` - - [ ] **Q1.3c** Add coverage comment to PR (fail if <85%) - - [ ] **Q1.3d** Upload test artifacts - - [ ] Commit: "feat(ci): add test execution with coverage" - - [ ] **Q1.4** [Brent] Add quality gates: - - [ ] **Q1.4a** Create `scripts/check-quality-gates.py` - - [ ] **Q1.4b** Fail if coverage <85% - - [ ] **Q1.4c** Fail if any type errors - - [ ] **Q1.4d** Fail if any security issues - - [ ] **Q1.4e** Generate summary comment for PR - - [ ] Commit: "feat(ci): add quality gate enforcement" - - [ ] **Q1.5** [Brent] Document branch protection rules: - - [ ] **Q1.5a** Require PR validation to pass - - [ ] **Q1.5b** Require 1 review (selective by Brent) - - [ ] **Q1.5c** Document in `docs/development/ci-cd.md` - - [ ] Commit: "docs(ci): add branch protection guide" - - [ ] Tests: Verify CI pipeline works - - [ ] **Q1.6** [Rui] Test CI pipeline with sample PRs: - - [ ] PR with perfect code (should pass) - - [ ] PR with type errors (should fail with annotations) - - [ ] PR with low coverage (should fail with comment) - - [ ] PR with security issues (should fail) +- [ ] **COMMIT (Owner: Brent | Group: Q0-Minimum) - Commit message: "feat(qa): add pre-commit baseline hooks"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Brent]: Add `pre-commit>=3.6.0` to `pyproject.toml` dev dependencies and ensure it is included in the `dev` extra. + - [ ] Code [Brent]: Create `.pre-commit-config.yaml` pinned to specific hook versions; include `ruff format`, `ruff check`, `pyright`, `check-merge-conflict`, `end-of-file-fixer`, `trailing-whitespace`. + - [ ] Code [Brent]: Add `pyrightconfig.json` validation to pre-commit (hook that fails if config missing or invalid). + - [ ] Code [Brent]: Add/confirm `nox -s lint` session that runs Ruff + pyright using project settings; ensure session exits non-zero on warnings. + - [ ] Code [Brent]: Add/confirm `nox -s format` session for Ruff formatting and align it with pre-commit `ruff format` behavior. + - [ ] Docs [Brent]: Update `CONTRIBUTING.md` with pre-commit install + run steps (no helper scripts). + - [ ] Tests (Behave) [Rui]: Add scenarios in `features/quality_automation.feature` that parse `.pre-commit-config.yaml`, assert required hooks are present, and verify pinned versions. + - [ ] Tests (Robot) [Rui]: Add `robot/quality_automation.robot` that runs `nox -s lint` and asserts zero failures. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/precommit_config_bench.py` to benchmark config parsing and hook list extraction. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Brent]: `git commit -m "feat(qa): add pre-commit baseline hooks"`. -- [ ] **Stage Q2: Advanced Automation** (Day 3) **[Brent]** - - [ ] Code: Set up advanced quality monitoring - - [ ] **Q2.1** [Brent] Create nightly quality workflow: - - [ ] **Q2.1a** Schedule for midnight UTC - - [ ] **Q2.1b** Run full test suite including slow tests - - [ ] **Q2.1c** Generate quality trend reports - - [ ] Commit: "feat(ci): add nightly quality checks" - - [ ] **Q2.2** [Brent] Add complexity monitoring: - - [ ] **Q2.2a** Install `radon>=6.0.1` for complexity metrics - - [ ] **Q2.2b** Add complexity checks to pre-commit - - [ ] **Q2.2c** Fail if cyclomatic complexity >10 - - [ ] Commit: "feat(qa): add complexity monitoring" - - [ ] **Q2.3** [Brent] Create quality dashboard: - - [ ] **Q2.3a** Script to aggregate metrics - - [ ] **Q2.3b** Track coverage trends - - [ ] **Q2.3c** Track type coverage - - [ ] **Q2.3d** Generate weekly reports - - [ ] Commit: "feat(qa): add quality dashboard" - - [ ] **Q2.4** [Brent] Add ADR compliance checking: - - [ ] **Q2.4a** Script to verify ADR compliance - - [ ] **Q2.4b** Check async usage per ADR-002 - - [ ] **Q2.4c** Check DI usage per ADR-003 - - [ ] **Q2.4d** Add to CI pipeline - - [ ] Commit: "feat(qa): add ADR compliance checks" - - [ ] **Q2.5** [Brent] Create PR template: - - [ ] **Q2.5a** Add `.github/pull_request_template.md` - - [ ] **Q2.5b** Include quality checklist - - [ ] **Q2.5c** Require testing description - - [ ] Commit: "feat(qa): add PR template" - - [ ] Documentation: Quality automation guide - - [ ] **Q2.6** [Brent] Document quality automation: - - [ ] **Q2.6a** Create `docs/development/quality-automation.md` - - [ ] **Q2.6b** Document all hooks and checks - - [ ] **Q2.6c** Add troubleshooting guide - - [ ] **Q2.6d** Add to developer onboarding - - [ ] Commit: "docs(qa): comprehensive quality guide" +- [ ] **COMMIT (Owner: Brent | Group: Q0-Minimum) - Commit message: "feat(ci): add nox-based PR validation workflow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Brent]: Update `.forgejo/workflows/ci.yml` (or create `.github/workflows/pr-validation.yml` if required) to install dependencies via Hatch and run `nox` (unit + integration + typecheck + lint + coverage_report). + - [ ] Code [Brent]: Ensure CI uses Python 3.13, caches pip/Hatch artifacts, and uploads `nox` logs on failure. + - [ ] Code [Brent]: Fail pipeline if any `nox` session fails or coverage <97% (explicit coverage gate). + - [ ] Docs [Brent]: Add CI usage notes in `docs/development/ci-cd.md`, including local repro commands and cache notes. + - [ ] Tests (Behave) [Rui]: Add a scenario that validates the workflow file exists and references required `nox` sessions. + - [ ] Tests (Robot) [Rui]: Add a Robot smoke test that runs the same `nox` session matrix locally and asserts zero failures. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/ci_yaml_parse_bench.py` to benchmark workflow parsing and key lookup. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Brent]: `git commit -m "feat(ci): add nox-based PR validation workflow"`. -**After Day 3**: Brent transitions to selective manual review (Days 4-8) focusing only on: -- Architectural decisions and design patterns -- Complex algorithms and business logic -- API contracts and interfaces -- Security-sensitive code paths +- [ ] **COMMIT (Owner: Brent | Group: Q0-Minimum) - Commit message: "feat(qa): enforce coverage >=97%"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Brent]: Update `nox -s coverage_report` (or equivalent session) to fail when coverage <97% and emit a clear error message. + - [ ] Code [Brent]: Wire coverage threshold enforcement into CI summary output (explicit failure line for parsing). + - [ ] Docs [Brent]: Update `docs/development/testing.md` with new coverage requirement and sample output. + - [ ] Tests (Behave) [Rui]: Add a scenario that parses coverage config and asserts threshold >=97%. + - [ ] Tests (Robot) [Rui]: Add a Robot test that runs `nox -s coverage_report` and asserts pass/fail behavior. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/coverage_report_bench.py` for coverage report runtime baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Brent]: `git commit -m "feat(qa): enforce coverage >=97%"`. -**After Day 8**: Brent transitions to validation testing support, working with Luis on: -- Edge case identification and testing -- Semantic validation implementation -- Performance testing for large codebases -- Integration test scenarios +**Parallel Group Q0-Advanced Gates [Brent - AFTER M1]** + +- [ ] **COMMIT (Owner: Brent | Group: Q0-Advanced) - Commit message: "feat(qa): add security scanning hooks"** (After M1) + - [ ] Code [Brent]: Add `bandit[toml]>=1.7.5` and `semgrep` dev dependencies; configure rules in `pyproject.toml`. + - [ ] Code [Brent]: Add pre-commit hooks for Bandit + Semgrep with minimal safe ruleset and explicit exclude patterns. + - [ ] Code [Brent]: Add `nox -s security` session that runs Bandit + Semgrep with config files. + - [ ] Docs [Brent]: Document security scan expectations in `docs/development/quality-automation.md`. + - [ ] Tests (Behave) [Rui]: Add scenario verifying Bandit/Semgrep hooks are declared in `.pre-commit-config.yaml`. + - [ ] Tests (Robot) [Rui]: Add Robot test that runs `nox -s security` (create session if missing). + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_scan_bench.py` to baseline scan runtime. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Brent]: `git commit -m "feat(qa): add security scanning hooks"`. + +- [ ] **COMMIT (Owner: Brent | Group: Q0-Advanced) - Commit message: "feat(qa): add complexity monitoring"** (After M1) + - [ ] Code [Brent]: Add `radon>=6.0.1` and a `nox -s complexity` session with threshold <=10. + - [ ] Code [Brent]: Add complexity check to CI matrix (non-blocking until M3) and print summary. + - [ ] Docs [Brent]: Document complexity thresholds and exceptions policy. + - [ ] Tests (Behave) [Rui]: Add scenario that asserts radon configuration exists. + - [ ] Tests (Robot) [Rui]: Add Robot test that runs `nox -s complexity` on a fixture module. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/complexity_scan_bench.py` for radon runtime baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Brent]: `git commit -m "feat(qa): add complexity monitoring"`. + +- [ ] **COMMIT (Owner: Brent | Group: Q0-Advanced) - Commit message: "docs(qa): add quality automation guide"** (After M1) + - [ ] Docs [Brent]: Create `docs/development/quality-automation.md` with hook lists, CI steps, and troubleshooting. + - [ ] Docs [Brent]: Link the guide from `README.md` and `CONTRIBUTING.md`. + - [ ] Tests (Behave) [Rui]: Add scenario verifying the guide exists and is linked. + - [ ] Tests (Robot) [Rui]: Add Robot doc build smoke test via `nox -s docs`. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/docs_build_bench.py` for docs build runtime baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Brent]: `git commit -m "docs(qa): add quality automation guide"`. --- @@ -1303,44 +1285,49 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **PARALLEL SUBTRACK A2b.beta [Luis]**: Plan metadata alignment + action linkage **PARALLEL SUBTRACK A2b.gamma [Aditya]**: Action YAML schema + examples (config-first) **SEQUENTIAL MERGE NOTE**: A2b.alpha + A2b.beta must land before A4b CLI wiring. - - [ ] **COMMIT (Owner: Jeff | Group: A2b.alpha) - Commit message: "feat(domain): align action metadata with invariants and automation profiles"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Update `src/cleveragents/domain/models/core/action.py` docstring to state actions are defined via YAML config and registered via CLI (remove "NOT YAML" wording). - - [ ] Code [Jeff]: Add `automation_profile` field (namespaced name string) to `Action` and validate `/` format + `local/` default handling. - - [ ] Code [Jeff]: Add `invariant_actor` (optional actor ref) and `invariants` list (action-scoped) with trimming, de-duplication, and empty-string rejection. - - [ ] Code [Jeff]: Add `definition_of_done_template` to preserve the pre-rendered DoD string before arg substitution; keep `definition_of_done` as rendered output. - - [ ] Code [Jeff]: Extend `ActionArgument` validation to enforce `min_value <= max_value`, regex only for string args, and default value type checks. - - [ ] Code [Jeff]: Add `Action.to_template_context()` (or equivalent) to generate deterministic arg context for templating. - - [ ] Docs [Jeff]: Update or create `docs/reference/action_model.md` with new fields, examples, and invariants/automation profile semantics. - - [ ] Tests (Behave) [Rui]: Add scenarios in `features/action_model.feature` for invariants validation, automation profile parsing, and definition_of_done_template retention. - - [ ] Tests (Robot) [Rui]: Add Robot scenario that loads action YAML and asserts invariants/automation profile fields are surfaced in CLI output (wired in A4b). - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_model_bench.py` to benchmark action argument parsing + template rendering. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(domain): align action metadata with invariants and automation profiles"`. - - [ ] **COMMIT (Owner: Luis | Group: A2b.beta) - Commit message: "feat(domain): align plan metadata with action linkage and automation profiles"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add `action_name` (namespaced name string) and `action_id` (ULID string) to `Plan` for traceability to the originating action. - - [ ] Code [Luis]: Add `automation_profile`, `invariant_actor`, and `invariants` fields to `Plan` with source tags (action/project/plan/global). - - [ ] Code [Luis]: Add `arguments` map (validated JSON-serializable values) and `definition_of_done_template` capture to the plan model for later re-rendering. - - [ ] Code [Luis]: Add execution metadata placeholders: `changeset_id`, `sandbox_refs`, `validation_summary`, and `decision_root_id` (optional until later stages). - - [ ] Code [Luis]: Enforce automation profile immutability after `plan use` and when phase progresses beyond Strategize. - - [ ] Docs [Luis]: Update `docs/reference/plan_model.md` to document new fields, immutability rules, and action linkage. - - [ ] Tests (Behave) [Rui]: Add scenarios in `features/plan_model.feature` for automation profile lock, invariant persistence, and action linkage fields. - - [ ] Tests (Robot) [Rui]: Add Robot scenario to inspect `plan status` output for action linkage and automation profile fields (once CLI aligned). - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_model_bench.py` for plan validation and serialization. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(domain): align plan metadata with action linkage and automation profiles"`. - - [ ] **COMMIT (Owner: Aditya | Group: A2b.gamma) - Commit message: "docs(action): add action YAML schema and examples"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Docs [Aditya]: Author `docs/schema/action.schema.yaml` with versioning, required fields, and explicit type constraints for actors, invariants, and arguments. - - [ ] Docs [Aditya]: Add example action configs under `examples/actions/` (simple, invariant-heavy, multi-project, and estimation-actor examples). - - [ ] Code [Aditya]: Add schema validation helper in `src/cleveragents/action/schema.py` that loads YAML, validates schema version, and returns typed data. - - [ ] Code [Aditya]: Add clear error messages for missing required fields and invalid namespaced names. - - [ ] Tests (Behave) [Rui]: Add scenarios that load each example YAML and assert schema validation passes; add invalid schema cases. - - [ ] Tests (Robot) [Rui]: Add a Robot smoke test that reads example YAML files and reports parse success/failure. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_schema_bench.py` for YAML schema validation throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Aditya]: `git commit -m "docs(action): add action YAML schema and examples"`. +- [ ] **COMMIT (Owner: Jeff | Group: A2b.alpha) - Commit message: "feat(domain): align action metadata with invariants and automation profiles"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Update `src/cleveragents/domain/models/core/action.py` docstring to state actions are defined via YAML config and registered via CLI (remove "NOT YAML" wording). + - [ ] Code [Jeff]: Add `automation_profile` field (namespaced name string) to `Action` and validate `/` format + `local/` default handling. + - [ ] Code [Jeff]: Add `invariant_actor` (optional actor ref) and `invariants` list (action-scoped) with trimming, de-duplication, and empty-string rejection. + - [ ] Code [Jeff]: Add `definition_of_done_template` to preserve the pre-rendered DoD string before arg substitution; keep `definition_of_done` as rendered output. + - [ ] Code [Jeff]: Extend `ActionArgument` validation to enforce `min_value <= max_value`, regex only for string args, and default value type checks; reject defaults that violate regex. + - [ ] Code [Jeff]: Add `ActionArgument.coerce_value()` helper that converts CLI/YAML strings into typed values (int/float/bool/list) with clear errors. + - [ ] Code [Jeff]: Add `Action.to_template_context()` (or equivalent) to generate deterministic arg context for templating and preserve ordering for tests. + - [ ] Code [Jeff]: Update `Action.validate_arguments()` to use default values when optional args are omitted and to include regex/min/max checks in error output. + - [ ] Code [Jeff]: Update `PlanLifecycleService.create_action()` to accept invariants, invariant_actor, automation_profile, and definition_of_done_template and pass them into the domain model. + - [ ] Docs [Jeff]: Update or create `docs/reference/action_model.md` with new fields, YAML-first guidance, and invariants/automation profile semantics. + - [ ] Tests (Behave) [Rui]: Add scenarios in `features/action_model.feature` for invariants validation, automation profile parsing, definition_of_done_template retention, and default value coercion. + - [ ] Tests (Robot) [Rui]: Add Robot scenario that loads action YAML and asserts invariants/automation profile fields are surfaced in CLI output (wired in A4b). + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_model_bench.py` to benchmark action argument parsing + template rendering. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(domain): align action metadata with invariants and automation profiles"`. +- [ ] **COMMIT (Owner: Luis | Group: A2b.beta) - Commit message: "feat(domain): align plan metadata with action linkage and automation profiles"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add `action_name` (namespaced name string) and `action_id` (ULID string) to `Plan` for traceability to the originating action. + - [ ] Code [Luis]: Add `automation_profile`, `invariant_actor`, and `invariants` fields to `Plan` with explicit source tags (action/project/plan/global) and ordering rules. + - [ ] Code [Luis]: Add `arguments` map (validated JSON-serializable values) and `definition_of_done_template` capture to the plan model for later re-rendering. + - [ ] Code [Luis]: Add execution metadata placeholders: `changeset_id`, `sandbox_refs`, `validation_summary`, and `decision_root_id` (optional until later stages). + - [ ] Code [Luis]: Add `Plan.validate_immutable_fields()` to enforce automation profile immutability after `plan use` and when phase progresses beyond Strategize. + - [ ] Code [Luis]: Update `PlanLifecycleService.use_action()` to populate action linkage, arguments, invariants, and automation profile on the Plan. + - [ ] Docs [Luis]: Update `docs/reference/plan_model.md` to document new fields, immutability rules, and action linkage. + - [ ] Tests (Behave) [Rui]: Add scenarios in `features/plan_model.feature` for automation profile lock, invariant persistence, action linkage fields, and argument serialization. + - [ ] Tests (Robot) [Rui]: Add Robot scenario to inspect `plan status` output for action linkage and automation profile fields (once CLI aligned). + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_model_bench.py` for plan validation and serialization. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(domain): align plan metadata with action linkage and automation profiles"`. +- [ ] **COMMIT (Owner: Aditya | Group: A2b.gamma) - Commit message: "docs(action): add action YAML schema and examples"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Docs [Aditya]: Author `docs/schema/action.schema.yaml` with versioning, required fields, and explicit type constraints for actors, invariants, arguments, and automation profiles. + - [ ] Docs [Aditya]: Add example action configs under `examples/actions/` (simple, invariant-heavy, multi-project, estimation-actor, and read-only examples). + - [ ] Code [Aditya]: Add schema validation helper in `src/cleveragents/action/schema.py` that loads YAML, validates schema version, and returns typed data. + - [ ] Code [Aditya]: Add clear error messages for missing required fields, invalid namespaced names, and invalid argument type combos. + - [ ] Code [Aditya]: Add unit helper to normalize YAML keys (snake_case vs camelCase) before validation. + - [ ] Tests (Behave) [Rui]: Add scenarios that load each example YAML and assert schema validation passes; add invalid schema cases (missing actor, invalid namespaced name, bad arg types). + - [ ] Tests (Robot) [Rui]: Add a Robot smoke test that reads example YAML files and reports parse success/failure. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_schema_bench.py` for YAML schema validation throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Aditya]: `git commit -m "docs(action): add action YAML schema and examples"`. - [x] **Stage A3: Plan State Machine** (Day 1-2) - COMPLETED 2026-02-05 - [x] Code: Implement plan lifecycle state machine @@ -1371,181 +1358,196 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group A4b: Action/Plan CLI Spec Alignment + Tests (M1-critical)** **PARALLEL SUBTRACK A4b.alpha [Jeff]**: CLI feature alignment **PARALLEL SUBTRACK A4b.beta [Rui]**: Behave + Robot coverage - - [ ] **COMMIT (Owner: Jeff | Group: A4b.alpha) - Commit message: "feat(cli): support action create from YAML config"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add `--config/-c` to `agents action create`; load YAML via `action/schema.py` and fail fast on schema violations. - - [ ] Code [Jeff]: Implement override precedence (CLI flags override YAML fields; explicit CLI empty string clears YAML value). - - [ ] Code [Jeff]: Normalize namespaced names, actor refs, and argument definitions from YAML into `ActionArgument` objects. - - [ ] Code [Jeff]: Add `--update` guard (if action exists) or explicit error per spec; ensure idempotent update path is clear. - - [ ] Docs [Jeff]: Update CLI reference with YAML-based action creation + override examples and failure messages. - - [ ] Tests (Behave) [Rui]: Add scenarios in `features/action_cli.feature` covering valid config, overrides, invalid schema, and missing required fields. - - [ ] Tests (Robot) [Rui]: Add `robot/action_cli_from_config.robot` with end-to-end CLI flow and output assertions. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_cli_config_bench.py` for config parsing + normalization. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(cli): support action create from YAML config"`. - - [ ] **COMMIT (Owner: Jeff | Group: A4b.alpha) - Commit message: "feat(cli): extend plan use with invariants and automation profile"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add `--automation-profile`, `--invariant`, and `--invariant-actor` flags to `agents plan use` and plumb into PlanLifecycleService. - - [ ] Code [Jeff]: Resolve action name -> action_id, validate automation profile existence, and attach plan-scoped invariants. - - [ ] Code [Jeff]: Persist resolved invariants + profile in Plan metadata for later Strategize/Execute steps. - - [ ] Docs [Jeff]: Update `docs/reference/plan_cli.md` with examples for profile + invariants and error cases. - - [ ] Tests (Behave) [Rui]: Add scenarios in `features/plan_lifecycle_cli.feature` covering profile selection, invariant validation, and multiple projects. - - [ ] Tests (Robot) [Rui]: Add Robot tests for plan use with invariants and automation profiles (positive + negative cases). - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_use_cli_bench.py` for argument parsing and validation. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(cli): extend plan use with invariants and automation profile"`. - - [ ] **COMMIT (Owner: Rui | Group: A4b.beta) - Commit message: "test(cli): add plan lifecycle Behave and Robot coverage"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Tests (Behave) [Rui]: Add full coverage for `plan execute`, `plan apply`, `plan status`, `plan list`, `plan cancel` (success + error paths). - - [ ] Tests (Robot) [Rui]: Add `robot/plan_lifecycle_cli.robot` covering end-to-end lifecycle transitions. - - [ ] Docs [Rui]: Update `docs/development/testing.md` with new CLI suites and command mappings. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_cli_smoke_bench.py` for CLI argument parsing overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "test(cli): add plan lifecycle Behave and Robot coverage"`. +- [ ] **COMMIT (Owner: Jeff | Group: A4b.alpha) - Commit message: "feat(cli): support action create from YAML config"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add `--config/-c` to `agents action create`; load YAML via `src/cleveragents/action/schema.py` and fail fast on schema violations. + - [ ] Code [Jeff]: Implement override precedence (CLI flags override YAML fields; explicit CLI empty string clears YAML value; CLI omits leave YAML as-is). + - [ ] Code [Jeff]: Normalize namespaced names, actor refs, and argument definitions from YAML into `ActionArgument` objects; surface errors with field path. + - [ ] Code [Jeff]: Add `--update` guard (if action exists) or explicit error per spec; ensure idempotent update path is clear. + - [ ] Code [Jeff]: Update `_print_action` output in `src/cleveragents/cli/commands/action.py` to show invariants, invariant_actor, and automation_profile. + - [ ] Docs [Jeff]: Update CLI reference with YAML-based action creation + override examples and failure messages. + - [ ] Tests (Behave) [Rui]: Add scenarios in `features/action_cli.feature` covering valid config, overrides, invalid schema, missing required fields, and update conflict. + - [ ] Tests (Robot) [Rui]: Add `robot/action_cli_from_config.robot` with end-to-end CLI flow and output assertions (includes invariants/profile display). + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_cli_config_bench.py` for config parsing + normalization. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(cli): support action create from YAML config"`. +- [ ] **COMMIT (Owner: Jeff | Group: A4b.alpha) - Commit message: "feat(cli): extend plan use with invariants and automation profile"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Change `agents plan use` signature to accept positional `` arguments per spec and keep `--project` as a legacy alias only until A5.legacy removal. + - [ ] Code [Jeff]: Add `--automation-profile`, `--invariant`, and `--invariant-actor` flags to `agents plan use` and plumb into PlanLifecycleService. + - [ ] Code [Jeff]: Resolve action name -> action_id, validate automation profile existence via AutomationProfileService, and attach plan-scoped invariants. + - [ ] Code [Jeff]: Persist resolved invariants + profile in Plan metadata for later Strategize/Execute steps; include invariant source tags. + - [ ] Docs [Jeff]: Update `docs/reference/plan_cli.md` with examples for profile + invariants, positional project usage, and error cases. + - [ ] Tests (Behave) [Rui]: Add scenarios in `features/plan_lifecycle_cli.feature` covering profile selection, invariant validation, positional project args, and multiple projects. + - [ ] Tests (Robot) [Rui]: Add Robot tests for plan use with invariants and automation profiles (positive + negative cases). + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_use_cli_bench.py` for argument parsing and validation. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(cli): extend plan use with invariants and automation profile"`. +- [ ] **COMMIT (Owner: Rui | Group: A4b.beta) - Commit message: "test(cli): add plan lifecycle Behave and Robot coverage"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Tests (Behave) [Rui]: Add full coverage for `plan execute`, `plan apply`, `plan status`, `plan list`, `plan cancel` (success + error paths, invalid phase, missing plan, multiple plans ready). + - [ ] Tests (Robot) [Rui]: Add `robot/plan_lifecycle_cli.robot` covering end-to-end lifecycle transitions with real DB persistence. + - [ ] Docs [Rui]: Update `docs/development/testing.md` with new CLI suites and command mappings. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_cli_smoke_bench.py` for CLI argument parsing overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "test(cli): add plan lifecycle Behave and Robot coverage"`. **Parallel Group A5: Plan Persistence (M1-critical)** **PARALLEL SUBTRACK A5.alpha [Jeff]**: Alembic migrations for action/plan tables **PARALLEL SUBTRACK A5.beta [Luis]**: SQLAlchemy models for new tables **SEQUENTIAL AFTER alpha+beta [Jeff + Luis]**: Repositories + service integration **PARALLEL CONTINUOUS [Rui]**: Persistence tests added inside each commit - - [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add actions and action_invariants tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add Alembic migration for `actions` table with ULID PK, namespaced_name, actor refs, DoD fields, automation_profile, invariant_actor, timestamps. - - [ ] Code [Jeff]: Add `action_invariants` table with FK to actions, `scope` column, and created_at timestamp. - - [ ] Code [Jeff]: Add unique index on actions.namespaced_name and search index on namespace for list filtering. - - [ ] Code [Jeff]: Ensure downgrade path drops indexes and tables in reverse order. - - [ ] Docs [Jeff]: Update `docs/reference/database_schema.md` with column-level details and constraints. - - [ ] Tests (Behave) [Rui]: Add migration scenario that runs upgrade and asserts tables + indexes exist. - - [ ] Tests (Robot) [Rui]: Add Robot migration smoke test using `nox -s db_migrate` (create session if missing). - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_actions_bench.py` for migration runtime baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(db): add actions and action_invariants tables"`. - - [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add lifecycle_plans and plan_projects tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add Alembic migration for `lifecycle_plans` with ULID PK, phase/state enums, action linkage, automation_profile, invariant_actor, and timestamps. - - [ ] Code [Jeff]: Add `plan_projects` table with plan_id, project_name (namespaced), read_only flag, and alias. - - [ ] Code [Jeff]: Add indexes on plan phase/state for filtering and plan_projects.project_name for lookups. - - [ ] Docs [Jeff]: Update schema docs with plan/project link rules and uniqueness constraints. - - [ ] Tests (Behave) [Rui]: Add migration scenario verifying plan/project link table + indexes. - - [ ] Tests (Robot) [Rui]: Add Robot test that inserts a plan/project link row and queries it. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_plans_bench.py` for migration runtime baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(db): add lifecycle_plans and plan_projects tables"`. - - [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add plan arguments and plan invariants tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add `plan_arguments` table (plan_id, name, value_json, value_type) and `plan_invariants` table (plan_id, invariant_text, source_scope). - - [ ] Code [Jeff]: Add uniqueness constraint on (plan_id, name) for arguments and (plan_id, invariant_text) for invariants. - - [ ] Docs [Jeff]: Document argument storage, JSON serialization rules, and invariant source scopes. - - [ ] Tests (Behave) [Rui]: Add migration scenario verifying both tables and constraints. - - [ ] Tests (Robot) [Rui]: Add Robot test that inserts a plan invariant and asserts retrieval. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_plan_args_bench.py` for migration runtime baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(db): add plan arguments and plan invariants tables"`. - - [ ] **COMMIT (Owner: Luis | Group: A5.beta) - Commit message: "feat(models): add action and lifecycle plan ORM models"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add SQLAlchemy models for Action, ActionInvariant, LifecyclePlan, PlanProjectLink, PlanArgument, PlanInvariant. - - [ ] Code [Luis]: Implement `to_domain()` and `from_domain()` mappings with ULID validation, enum conversion, and timestamp normalization. - - [ ] Code [Luis]: Add repository-facing helpers for filtering by namespace/phase/state. - - [ ] Docs [Luis]: Update ORM mapping notes in `docs/reference/database_schema.md` with model field mapping table. - - [ ] Tests (Behave) [Rui]: Add scenarios for ORM round-trip serialization and enum conversions. - - [ ] Tests (Robot) [Rui]: Add Robot test that loads a plan and asserts field mapping correctness. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/orm_mapping_bench.py` for Action/Plan mapping throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(models): add action and lifecycle plan ORM models"`. - - [ ] **COMMIT (Owner: Jeff | Group: A5.gamma) - Commit message: "feat(repo): add action and lifecycle plan repositories"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Implement ActionRepository CRUD + list filters by namespace/state/automation profile. - - [ ] Code [Jeff]: Implement PlanRepository CRUD + list filters by phase/state/project; add plan lookup by namespaced name. - - [ ] Code [Jeff]: Add retry decorator to repositories; retry only on `OperationalError`, never on `IntegrityError`. - - [ ] Docs [Jeff]: Document repository interfaces, error types, and pagination guidance. - - [ ] Tests (Behave) [Rui]: Add scenarios for repository create/get/list/update/delete guardrails. - - [ ] Tests (Robot) [Rui]: Add Robot test that exercises repository through service layer. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/repository_query_bench.py` for list + filter performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(repo): add action and lifecycle plan repositories"`. - - [ ] **COMMIT (Owner: Luis | Group: A5.gamma) - Commit message: "feat(service): persist plan lifecycle via repositories"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Update `PlanLifecycleService` to use repositories instead of in-memory dicts. - - [ ] Code [Luis]: Ensure plan creation persists arguments, invariants, automation profile, and project links in a single transaction. - - [ ] Code [Luis]: Add transactional safeguards for multi-step updates (create action + plan, correction updates). - - [ ] Docs [Luis]: Update service docs to reflect persistence and remove in-memory notes. - - [ ] Tests (Behave) [Rui]: Add scenarios for persisted lifecycle transitions and error handling. - - [ ] Tests (Robot) [Rui]: Add end-to-end test that restarts the app and re-reads plan state. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_lifecycle_service_bench.py` for persistence operations. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(service): persist plan lifecycle via repositories"`. - - [ ] **COMMIT (Owner: Luis | Group: A5.gamma) - Commit message: "feat(di): wire lifecycle repos and services"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Register ActionRepository + PlanRepository in `application/container.py` and UnitOfWork. - - [ ] Code [Luis]: Inject repositories into PlanLifecycleService and CLI commands. - - [ ] Code [Luis]: Add container wiring tests to ensure singleton lifetimes are correct. - - [ ] Docs [Luis]: Update DI wiring notes in `docs/architecture/decisions/adr-003.md`. - - [ ] Tests (Behave) [Rui]: Add scenarios that use container wiring for lifecycle commands. - - [ ] Tests (Robot) [Rui]: Add Robot smoke test verifying CLI uses persisted service. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/di_container_bench.py` for container resolution overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(di): wire lifecycle repos and services"`. + **SEQUENTIAL NOTE**: `action_arguments` migration must land after `actions` migration; A5.legacy should land after A4b CLI alignment + A5.gamma persistence wiring. +- [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add actions and action_invariants tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add Alembic migration for `actions` table with ULID PK, namespaced_name, actor refs (strategy/execution/review/apply/estimation), DoD fields, automation_profile, invariant_actor, reusable/read_only flags, tags_json, created_by, timestamps. + - [ ] Code [Jeff]: Add `action_invariants` table with FK to actions, `scope` column, `invariant_text`, and created_at timestamp. + - [ ] Code [Jeff]: Add unique index on actions.namespaced_name and search index on namespace for list filtering. + - [ ] Code [Jeff]: Ensure downgrade path drops indexes and tables in reverse order. + - [ ] Docs [Jeff]: Update `docs/reference/database_schema.md` with column-level details and constraints. + - [ ] Tests (Behave) [Rui]: Add migration scenario that runs upgrade and asserts tables + indexes exist. + - [ ] Tests (Robot) [Rui]: Add Robot migration smoke test using `nox -s db_migrate` (create session if missing). + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_actions_bench.py` for migration runtime baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(db): add actions and action_invariants tables"`. +- [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add action_arguments table"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add Alembic migration for `action_arguments` table (action_id FK, name, type, requirement, description, default_value_json, min_value, max_value, validation_pattern). + - [ ] Code [Jeff]: Add uniqueness constraint on (action_id, name) and index on action_id. + - [ ] Docs [Jeff]: Update `docs/reference/database_schema.md` with action_arguments columns and constraints. + - [ ] Tests (Behave) [Rui]: Add migration scenario verifying action_arguments table and constraints. + - [ ] Tests (Robot) [Rui]: Add Robot migration smoke test that inserts a row and queries it. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_action_args_bench.py` for migration baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(db): add action_arguments table"`. +- [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add lifecycle_plans and plan_projects tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add Alembic migration for `lifecycle_plans` with ULID PK, phase/state enums, action linkage (action_id + action_name), automation_profile, invariant_actor, definition_of_done_template, and timestamps. + - [ ] Code [Jeff]: Add `plan_projects` table with plan_id, project_name (namespaced), read_only flag, and alias. + - [ ] Code [Jeff]: Add indexes on plan phase/state for filtering and plan_projects.project_name for lookups. + - [ ] Docs [Jeff]: Update schema docs with plan/project link rules and uniqueness constraints. + - [ ] Tests (Behave) [Rui]: Add migration scenario verifying plan/project link table + indexes. + - [ ] Tests (Robot) [Rui]: Add Robot test that inserts a plan/project link row and queries it. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_plans_bench.py` for migration runtime baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(db): add lifecycle_plans and plan_projects tables"`. +- [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add plan arguments and plan invariants tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add `plan_arguments` table (plan_id, name, value_json, value_type) and `plan_invariants` table (plan_id, invariant_text, source_scope). + - [ ] Code [Jeff]: Add uniqueness constraint on (plan_id, name) for arguments and (plan_id, invariant_text) for invariants. + - [ ] Docs [Jeff]: Document argument storage, JSON serialization rules, and invariant source scopes. + - [ ] Tests (Behave) [Rui]: Add migration scenario verifying both tables and constraints. + - [ ] Tests (Robot) [Rui]: Add Robot test that inserts a plan invariant and asserts retrieval. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_plan_args_bench.py` for migration runtime baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(db): add plan arguments and plan invariants tables"`. +- [ ] **COMMIT (Owner: Luis | Group: A5.beta) - Commit message: "feat(models): add action and lifecycle plan ORM models"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add SQLAlchemy models for Action, ActionInvariant, ActionArgument, LifecyclePlan, PlanProjectLink, PlanArgument, PlanInvariant. + - [ ] Code [Luis]: Implement `to_domain()` and `from_domain()` mappings with ULID validation, enum conversion, and timestamp normalization. + - [ ] Code [Luis]: Add repository-facing helpers for filtering by namespace/phase/state and eager-load action arguments. + - [ ] Docs [Luis]: Update ORM mapping notes in `docs/reference/database_schema.md` with model field mapping table. + - [ ] Tests (Behave) [Rui]: Add scenarios for ORM round-trip serialization and enum conversions. + - [ ] Tests (Robot) [Rui]: Add Robot test that loads a plan and asserts field mapping correctness. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/orm_mapping_bench.py` for Action/Plan mapping throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(models): add action and lifecycle plan ORM models"`. +- [ ] **COMMIT (Owner: Jeff | Group: A5.gamma) - Commit message: "feat(repo): add action and lifecycle plan repositories"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Implement ActionRepository CRUD + list filters by namespace/state/automation profile; include ActionArgument persistence. + - [ ] Code [Jeff]: Implement PlanRepository CRUD + list filters by phase/state/project; add plan lookup by namespaced name. + - [ ] Code [Jeff]: Add retry decorator to repositories; retry only on `OperationalError`, never on `IntegrityError`. + - [ ] Docs [Jeff]: Document repository interfaces, error types, and pagination guidance. + - [ ] Tests (Behave) [Rui]: Add scenarios for repository create/get/list/update/delete guardrails, including action argument round-trips. + - [ ] Tests (Robot) [Rui]: Add Robot test that exercises repository through service layer. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/repository_query_bench.py` for list + filter performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(repo): add action and lifecycle plan repositories"`. +- [ ] **COMMIT (Owner: Luis | Group: A5.gamma) - Commit message: "feat(service): persist plan lifecycle via repositories"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Update `PlanLifecycleService` to use repositories instead of in-memory dicts. + - [ ] Code [Luis]: Ensure plan creation persists arguments, invariants, automation profile, action linkage, and project links in a single transaction. + - [ ] Code [Luis]: Add transactional safeguards for multi-step updates (create action + plan, correction updates) and roll back on errors. + - [ ] Docs [Luis]: Update service docs to reflect persistence and remove in-memory notes. + - [ ] Tests (Behave) [Rui]: Add scenarios for persisted lifecycle transitions and error handling (duplicate names, invalid transitions). + - [ ] Tests (Robot) [Rui]: Add end-to-end test that restarts the app and re-reads plan state. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_lifecycle_service_bench.py` for persistence operations. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(service): persist plan lifecycle via repositories"`. +- [ ] **COMMIT (Owner: Luis | Group: A5.gamma) - Commit message: "feat(di): wire lifecycle repos and services"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Register ActionRepository + PlanRepository in `application/container.py` and UnitOfWork. + - [ ] Code [Luis]: Inject repositories into PlanLifecycleService and CLI commands; remove direct service instantiation in CLI. + - [ ] Code [Luis]: Add container wiring tests to ensure singleton lifetimes are correct. + - [ ] Docs [Luis]: Update DI wiring notes in `docs/architecture/decisions/adr-003.md`. + - [ ] Tests (Behave) [Rui]: Add scenarios that use container wiring for lifecycle commands. + - [ ] Tests (Robot) [Rui]: Add Robot smoke test verifying CLI uses persisted service. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/di_container_bench.py` for container resolution overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(di): wire lifecycle repos and services"`. - - [ ] **COMMIT (Owner: Rui | Group: A5.tests) - Commit message: "test(persistence): add plan/action persistence suites"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Tests (Behave) [Rui]: Add plan persistence scenarios (create, update phase/state, list filters, plan tree, concurrency). - - [ ] Tests (Behave) [Rui]: Add action persistence scenarios (create, list available, archive guard). - - [ ] Tests (Robot) [Rui]: Add plan persistence E2E (full lifecycle, restart persistence, concurrent CLI access). - - [ ] Docs [Rui]: Update `docs/development/testing.md` with persistence suites and `nox` commands. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/persistence_suites_bench.py` for DB read/write baselines. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "test(persistence): add plan/action persistence suites"`. +- [ ] **COMMIT (Owner: Rui | Group: A5.tests) - Commit message: "test(persistence): add plan/action persistence suites"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Tests (Behave) [Rui]: Add plan persistence scenarios (create, update phase/state, list filters, plan tree, concurrency). + - [ ] Tests (Behave) [Rui]: Add action persistence scenarios (create, list available, archive guard, action arguments persisted). + - [ ] Tests (Robot) [Rui]: Add plan persistence E2E (full lifecycle, restart persistence, concurrent CLI access). + - [ ] Docs [Rui]: Update `docs/development/testing.md` with persistence suites and `nox` commands. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/persistence_suites_bench.py` for DB read/write baselines. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "test(persistence): add plan/action persistence suites"`. **Parallel Group A5.legacy: Remove legacy plan build/apply path (M1-critical)** - - [ ] **COMMIT (Owner: Jeff | Group: A5.legacy) - Commit message: "refactor(plan): remove legacy plan service and CLI"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Remove `PlanService` usage from CLI (`plan tell/build/apply/new/current/list/cd/continue`). - - [ ] Code [Jeff]: Remove or quarantine legacy `plan_service.py`, `plan_legacy.py`, and legacy CLI helpers; add explicit NotImplementedError where needed. - - [ ] Code [Jeff]: Remove or archive legacy `PlanModel`/`PlanStatus` DB tables if unused by v3; document migration path. - - [ ] Docs [Jeff]: Update CLI docs to list only v3 lifecycle commands and new `plan use/execute/apply` flows. - - [ ] Tests (Behave) [Rui]: Remove/replace legacy scenarios with v3 equivalents and adjust coverage expectations. - - [ ] Tests (Robot) [Rui]: Remove legacy robot suites and add v3 replacements where needed. - - [ ] Tests (ASV) [Rui]: Update asv suite to remove legacy plan benchmarks and add v3 lifecycle baseline benchmark. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "refactor(plan): remove legacy plan service and CLI"`. +- [ ] **COMMIT (Owner: Jeff | Group: A5.legacy) - Commit message: "refactor(plan): remove legacy plan service and CLI"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Remove `PlanService` usage from CLI (`plan tell/build/apply/new/current/list/cd/continue`) and delete command handlers from `src/cleveragents/cli/commands/plan.py`. + - [ ] Code [Jeff]: Remove or quarantine legacy `plan_service.py`, `plan_legacy.py`, and legacy CLI helpers; add explicit NotImplementedError where needed. + - [ ] Code [Jeff]: Remove `PlanService` wiring from `src/cleveragents/application/container.py` and any references from `cli/commands/auto_debug.py`. + - [ ] Code [Jeff]: Rename `plan lifecycle-apply` to `plan apply` and update command wiring once legacy apply is removed. + - [ ] Code [Jeff]: Remove or archive legacy `PlanModel`/`PlanStatus` DB tables if unused by v3; document migration path. + - [ ] Docs [Jeff]: Update CLI docs to list only v3 lifecycle commands and new `plan use/execute/apply` flows. + - [ ] Tests (Behave) [Rui]: Remove/replace legacy scenarios with v3 equivalents and adjust coverage expectations. + - [ ] Tests (Robot) [Rui]: Remove legacy robot suites and add v3 replacements where needed. + - [ ] Tests (ASV) [Rui]: Update asv suite to remove legacy plan benchmarks and add v3 lifecycle baseline benchmark. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "refactor(plan): remove legacy plan service and CLI"`. **Parallel Group A6: Automation Profiles Foundation [Jeff + Luis]** (M1-critical; depends on A5 persistence) **PARALLEL SUBTRACK A6.core [Jeff]**: Profile model + built-ins + schema **PARALLEL SUBTRACK A6.service [Luis]**: Profile resolution + precedence **PARALLEL SUBTRACK A6.cli [Rui]**: CLI commands for profiles **SEQUENTIAL MERGE NOTE**: A6.core must land before A6.service/cli; A6.service must land before gating integration in Section 6. - - [ ] **COMMIT (Owner: Jeff | Group: A6.core) - Commit message: "feat(domain): add automation profile model and built-ins"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add `AutomationProfile` model with threshold fields (phase transitions, decision autonomy, child plan spawn, self-repair, apply gating). - - [ ] Code [Jeff]: Add built-in profiles (`manual`, `review`, `supervised`, `full-auto`, etc.) per spec with constant definitions. - - [ ] Code [Jeff]: Add YAML schema for automation profiles under `docs/schema/automation_profile.schema.yaml` and loader helper. - - [ ] Docs [Jeff]: Add `docs/reference/automation_profiles.md` describing built-ins and threshold semantics. - - [ ] Tests (Behave) [Rui]: Add scenarios for profile validation and built-in defaults. - - [ ] Tests (Robot) [Rui]: Add Robot test that loads each built-in profile and prints summary. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_bench.py` for profile validation. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(domain): add automation profile model and built-ins"`. - - [ ] **COMMIT (Owner: Luis | Group: A6.service) - Commit message: "feat(service): resolve automation profiles with precedence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add `AutomationProfileService` to resolve profiles with precedence (plan > action > project > global). - - [ ] Code [Luis]: Add persistence table `automation_profiles` (namespaced name PK) and repository with list/show. - - [ ] Code [Luis]: Add config key `core.automation_profile` and env var override for global default. - - [ ] Docs [Luis]: Update `docs/reference/config.md` with automation profile defaults and override behavior. - - [ ] Tests (Behave) [Rui]: Add scenarios for precedence resolution and missing profile errors. - - [ ] Tests (Robot) [Rui]: Add Robot config smoke test for global profile override. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_resolution_bench.py` for resolution latency. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(service): resolve automation profiles with precedence"`. - - [ ] **COMMIT (Owner: Rui | Group: A6.cli) - Commit message: "feat(cli): add automation-profile commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Rui]: Implement `agents automation-profile add/remove/list/show` commands with YAML config input. - - [ ] Code [Rui]: Add `--automation-profile` to `plan use` and output profile in `plan status`. - - [ ] Docs [Rui]: Update CLI reference with automation-profile command examples. - - [ ] Tests (Behave) [Rui]: Add CLI scenarios for profile add/list/show/remove. - - [ ] Tests (Robot) [Rui]: Add Robot CLI tests for automation-profile commands. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_cli_bench.py` for CLI parsing. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "feat(cli): add automation-profile commands"`. +- [ ] **COMMIT (Owner: Jeff | Group: A6.core) - Commit message: "feat(domain): add automation profile model and built-ins"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add `AutomationProfile` model with threshold fields (phase transitions, decision autonomy, child plan spawn, self-repair, apply gating) and validation for 0.0-1.0 ranges. + - [ ] Code [Jeff]: Add built-in profiles (`manual`, `review`, `supervised`, `full-auto`, etc.) per spec with constant definitions and stable IDs. + - [ ] Code [Jeff]: Add YAML schema for automation profiles under `docs/schema/automation_profile.schema.yaml` and loader helper. + - [ ] Docs [Jeff]: Add `docs/reference/automation_profiles.md` describing built-ins and threshold semantics. + - [ ] Tests (Behave) [Rui]: Add scenarios for profile validation and built-in defaults. + - [ ] Tests (Robot) [Rui]: Add Robot test that loads each built-in profile and prints summary. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_bench.py` for profile validation. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(domain): add automation profile model and built-ins"`. +- [ ] **COMMIT (Owner: Luis | Group: A6.service) - Commit message: "feat(service): resolve automation profiles with precedence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add `AutomationProfileService` to resolve profiles with precedence (plan > action > project > global). + - [ ] Code [Luis]: Add persistence table `automation_profiles` (namespaced name PK) and repository with list/show/update. + - [ ] Code [Luis]: Add config key `core.automation_profile` and env var override for global default. + - [ ] Docs [Luis]: Update `docs/reference/config.md` with automation profile defaults and override behavior. + - [ ] Tests (Behave) [Rui]: Add scenarios for precedence resolution and missing profile errors. + - [ ] Tests (Robot) [Rui]: Add Robot config smoke test for global profile override. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_resolution_bench.py` for resolution latency. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(service): resolve automation profiles with precedence"`. +- [ ] **COMMIT (Owner: Rui | Group: A6.cli) - Commit message: "feat(cli): add automation-profile commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Rui]: Implement `agents automation-profile add/remove/list/show` commands with YAML config input. + - [ ] Code [Rui]: Add `--automation-profile` to `plan use` and output profile in `plan status`. + - [ ] Docs [Rui]: Update CLI reference with automation-profile command examples. + - [ ] Tests (Behave) [Rui]: Add CLI scenarios for profile add/list/show/remove. + - [ ] Tests (Robot) [Rui]: Add Robot CLI tests for automation-profile commands. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_cli_bench.py` for CLI parsing. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "feat(cli): add automation-profile commands"`. **M1 SUCCESS CRITERIA (Day 7 MVP - source code only)**: - Action created from YAML config and persisted (namespaced name, invariants, automation profile). @@ -1566,6 +1568,8 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Hamza]: Add resource type fields: `user_addable`, `allowed_parents`, `allowed_children`, `auto_discover`, `handler` reference, and `sandbox_strategy` default. - [ ] Code [Hamza]: Add `Resource` and `ResourceRef` models with ULID, optional namespaced name, type name, location, description, sandbox strategy, read_only, and metadata. - [ ] Code [Hamza]: Add validators for namespaced naming, ULID format, and parent/child DAG sanity (no self loops, no duplicate edges, type compatibility). + - [ ] Code [Hamza]: Add `docs/schema/resource_type.schema.yaml` with CLI argument definitions, parent/child constraints, and handler metadata. + - [ ] Code [Hamza]: Add resource type YAML loader in `src/cleveragents/resource/schema.py` with version guard and clear error messages. - [ ] Docs [Hamza]: Add `docs/reference/resource_model.md` with examples for git-checkout and fs-directory resources plus physical/virtual notes. - [ ] Tests (Behave) [Rui]: Add scenarios validating ULID format, namespace rules, allowed parent/child type checks, and sandbox strategy defaults. - [ ] Tests (Robot) [Rui]: Add Robot test that loads a ResourceTypeSpec YAML fixture and validates it. @@ -1574,9 +1578,10 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(domain): add resource type spec and resource model"`. - [ ] **COMMIT (Owner: Hamza | Group: B1.core) - Commit message: "feat(domain): add project model v3 with linked resources"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Hamza]: Add `Project`, `ProjectResourceLink`, `ProjectValidation`, and `ProjectContextPolicy` models using namespaced name as the unique identifier (no ULID per spec). + - [ ] Code [Hamza]: Add `Project`, `ProjectResourceLink`, `ProjectValidationSummary` (derived from validation attachments), and `ProjectContextPolicy` models using namespaced name as the unique identifier (no ULID per spec). - [ ] Code [Hamza]: Add fields for `invariants`, `invariant_actor`, `automation_profile`, and `context_views` (strategize/execute/apply/default). - [ ] Code [Hamza]: Add validation for resource link overrides (read_only flags, alias uniqueness, resource existence). + - [ ] Code [Hamza]: Add helpers to compute effective invariants and automation profile (project defaults). - [ ] Docs [Hamza]: Add `docs/reference/project_model.md` describing resource linking, validation attachments, and context view policies. - [ ] Tests (Behave) [Rui]: Add scenarios for project model validation, link overrides, and context view inheritance. - [ ] Tests (Robot) [Rui]: Add Robot test that creates a Project object and prints serialized output. @@ -1598,7 +1603,8 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group B2: Project Persistence + Services [Hamza + Luis]** (depends on B1 domain models) - [ ] **COMMIT (Owner: Jeff | Group: B2.persistence) - Commit message: "feat(db): add projects and project links tables"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Add Alembic migration for `projects`, `project_resource_links`, and `project_validations` tables. + - [ ] Code [Jeff]: Add Alembic migration for `projects` and `project_resource_links` tables (no standalone project_validations table). + - [ ] Code [Jeff]: Store `automation_profile`, `invariant_actor`, `invariants_json`, and `context_policy_json` on `projects` table. - [ ] Code [Jeff]: Use namespaced name as project primary key; enforce unique constraint on `projects.namespaced_name`. - [ ] Code [Jeff]: Add indexes for `project_resource_links.project_name` and `resource_id` for fast joins. - [ ] Docs [Jeff]: Document project table schema and link semantics in `docs/reference/database_schema.md`. @@ -1621,9 +1627,9 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Commit [Hamza]: `git commit -m "feat(repo): add resource repositories"`. - [ ] **COMMIT (Owner: Hamza | Group: B2.persistence) - Commit message: "feat(repo): add project repositories"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Implement `ProjectRepository` and `ProjectResourceLinkRepository` with namespace filtering and name-based lookup. - - [ ] Code [Hamza]: Add methods to list project validations and context policies. + - [ ] Code [Hamza]: Add methods to list project context policies and derived validation attachment summaries for linked resources. - [ ] Docs [Hamza]: Update repository docs with project link examples and validation attachment notes. - - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/unlink and validation list. + - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/unlink and validation attachment summaries. - [ ] Tests (Robot) [Rui]: Add Robot test that links two resources to one project. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_repository_bench.py` for link/unlink performance. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). @@ -1642,10 +1648,10 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Commit [Hamza]: `git commit -m "feat(service): add resource registry service"`. - [ ] **COMMIT (Owner: Luis | Group: B2.service) - Commit message: "feat(service): add project service v3"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Implement `ProjectService` create/list/show/delete/link/unlink methods using repositories. - - [ ] Code [Luis]: Add validation attachment helpers and context policy setters for project views. + - [ ] Code [Luis]: Add validation attachment helpers (read-only listing of validation attachments for linked resources) and context policy setters for project views. - [ ] Code [Luis]: Enforce read-only resource links and project-level invariant actor defaults. - [ ] Docs [Luis]: Update `docs/reference/project_service.md` with usage examples and error cases. - - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/unlink/validation/context policy. + - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/unlink/context policy + validation attachment visibility. - [ ] Tests (Robot) [Rui]: Add Robot test that creates project and links a resource. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_service_bench.py` for link/unlink performance. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). @@ -1656,6 +1662,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] **COMMIT (Owner: Rui | Group: B3.cli) - Commit message: "feat(cli): add resource type commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Rui]: Add `agents resource type add/remove/list/show` commands with YAML config input and schema validation. - [ ] Code [Rui]: Implement `--update` behavior and error on name conflicts per spec. + - [ ] Code [Rui]: Wire `resource type add` to `ResourceTypeSpec` loader with clear error output and schema version guard. - [ ] Docs [Rui]: Update CLI reference with resource type examples and expected output columns. - [ ] Tests (Behave) [Rui]: Add scenarios for resource type lifecycle and invalid schema handling. - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/resource_type_cli.robot`. @@ -1666,8 +1673,9 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] **COMMIT (Owner: Rui | Group: B3.cli) - Commit message: "feat(cli): add resource commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Rui]: Add `agents resource add/remove/list/show/tree` commands with type-specific flags and name/ULID resolution. - [ ] Code [Rui]: Implement `resource inspect --tree/--file` per spec for resource introspection. + - [ ] Code [Rui]: Add `resource link-child` and `resource unlink-child` commands for DAG maintenance. - [ ] Docs [Rui]: Update CLI reference with resource examples (git-checkout, fs-directory) and output columns. - - [ ] Tests (Behave) [Rui]: Add scenarios for resource registration, list filters, and tree rendering. + - [ ] Tests (Behave) [Rui]: Add scenarios for resource registration, list filters, tree rendering, and link-child constraints. - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/resource_cli.robot`. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_cli_bench.py` for command parsing and list output. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). @@ -1675,15 +1683,30 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Commit [Rui]: `git commit -m "feat(cli): add resource commands"`. - [ ] **COMMIT (Owner: Rui | Group: B3.cli) - Commit message: "feat(cli): add project commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Rui]: Add `agents project create/show/list/delete/link-resource/unlink-resource` commands using namespaced project names. - - [ ] Code [Rui]: Add `agents project validation add/remove/list` and `project context set/show` commands (context views per phase). - - [ ] Docs [Rui]: Update CLI reference with project examples and validation output. - - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/validation/context policies. + - [ ] Code [Rui]: Add `project context set/show` commands (context views per phase) and ensure output includes linked resources + validation attachments. + - [ ] Docs [Rui]: Update CLI reference with project examples and validation attachment visibility (via `agents validation attach`). + - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/unlink/context policies and validation display. - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/project_cli.robot`. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_cli_bench.py` for command parsing and list output. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Rui]: `git commit -m "feat(cli): add project commands"`. +**Parallel Group B3.cleanup: Legacy Project Removal [Jeff]** (after B3.cli lands) + +- [ ] **COMMIT (Owner: Jeff | Group: B3.cleanup) - Commit message: "refactor(project): remove legacy project init/status commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Remove legacy `agents project init/status/clean/file-filter` commands from `src/cleveragents/cli/commands/project.py`. + - [ ] Code [Jeff]: Remove `.cleveragents` directory bootstrap logic from legacy `ProjectService` and deprecate `ProjectSettings` fields tied to local init. + - [ ] Code [Jeff]: Update `src/cleveragents/application/container.py` to stop wiring legacy ProjectService once v3 service is in place. + - [ ] Code [Jeff]: Remove legacy `src/cleveragents/domain/models/core/project.py` in favor of v3 project model and update imports. + - [ ] Docs [Jeff]: Remove references to `agents project init` from CLI docs and point to `agents project create` + `agents init` (global) flows. + - [ ] Tests (Behave) [Rui]: Remove/replace legacy project init scenarios with v3 project create scenarios. + - [ ] Tests (Robot) [Rui]: Remove legacy project init Robot suites and add v3 replacements if missing. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_cli_cleanup_bench.py` for CLI help/rendering baseline after removal. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "refactor(project): remove legacy project init/status commands"`. + **Parallel Group B4: Sandboxing [Luis + Jeff]** (depends on resource registry + project links) - [ ] **COMMIT (Owner: Luis | Group: B4.sandbox) - Commit message: "feat(sandbox): add sandbox strategy interface and manager"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Add `SandboxStrategy` protocol, `SandboxRef`, `SandboxManager`, and `SandboxRegistry` with per-resource sandboxes. @@ -1729,1565 +1752,19 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(sandbox): add copy_on_write strategy stub"`. -- [ ] **Stage B1: Project Data Model** (Day 1-2) **[Hamza - Python Expert, RDF Background]** - - **SEQUENTIAL ORDER**: B1.3 (ResourceType) → B1.4 (SandboxStrategy) → B1.2 (Resource) → B1.5 (ValidationConfig) → B1.6 (ContextConfig) → B1.1 (Project) - The order matters because Project depends on Resource, which depends on enums. - - - [ ] Code: Create Project domain model - - [ ] **B1.3** [Hamza] Define `ResourceType` enum in `src/cleveragents/domain/models/core/resource.py`: - - [ ] **B1.3a** [Hamza] Create the file with proper imports: - - [ ] Import `Enum` from enum module - - [ ] Import `str` for string mixin: `class ResourceType(str, Enum):` - - [ ] Add module docstring explaining resource types - - [ ] Commit: "feat(domain): create resource.py with ResourceType enum scaffold" - - [ ] **B1.3b** [Hamza] Define enum values with descriptive docstrings: - - [ ] `GIT_REPOSITORY = "git_repository"` - Git repo (local path or remote URL), supports worktree sandboxing - - [ ] `FILESYSTEM = "filesystem"` - Local directory or file tree, supports copy-on-write sandboxing - - [ ] `DATABASE = "database"` - SQL/NoSQL database endpoint, supports transaction-based sandboxing - - [ ] `API_ENDPOINT = "api_endpoint"` - REST/GraphQL API, typically cannot be sandboxed - - [ ] `DOCUMENT_CORPUS = "document_corpus"` - Collection of documents (PDFs, markdown, wikis) - - [ ] `CLOUD_INFRASTRUCTURE = "cloud_infrastructure"` - Cloud resources (AWS, GCP, Azure) - - [ ] Commit: "feat(domain): define ResourceType enum values" - - [ ] **B1.4** [Hamza] Define `SandboxStrategy` enum in same file: - - [ ] **B1.4a** [Hamza] Define enum with string values: - - [ ] `GIT_WORKTREE = "git_worktree"` - Use `git worktree add` for isolation, efficient for git repos - - [ ] `COPY_ON_WRITE = "copy_on_write"` - Copy directory to temp location, universal but can be slow for large dirs - - [ ] `OVERLAY = "overlay"` - Use overlayfs (Linux only), efficient copy-on-write for large directories - - [ ] `TRANSACTION_ROLLBACK = "transaction_rollback"` - Database transaction that can be rolled back - - [ ] `VERSIONING = "versioning"` - Use versioning features (e.g., S3 versioning) - - [ ] `NONE = "none"` - No sandboxing possible, modifications are immediate and irreversible - - [ ] Commit: "feat(domain): define SandboxStrategy enum values" - - [ ] **B1.4b** [Hamza] Add helper method to check sandbox capabilities: - - [ ] `@classmethod def supports_rollback(cls, strategy: 'SandboxStrategy') -> bool:` - Returns True for all except NONE - - [ ] `@classmethod def is_copy_based(cls, strategy: 'SandboxStrategy') -> bool:` - Returns True for COPY_ON_WRITE, OVERLAY - - [ ] Commit: "feat(domain): add SandboxStrategy helper methods" - - [ ] **B1.2** [Hamza] Define `Resource` Pydantic model in `src/cleveragents/domain/models/core/resource.py`: - - [ ] **B1.2a** [Hamza] Create basic model structure: - - [ ] Import `BaseModel, Field, field_validator` from pydantic - - [ ] Import `datetime` for timestamps - - [ ] Import `Any` from typing for metadata dict - - [ ] Create `class Resource(BaseModel):` with `model_config = ConfigDict(frozen=True)` - - [ ] Commit: "feat(domain): add Resource model scaffold" - - [ ] **B1.2b** [Hamza] Define all fields with proper types and descriptions: - - [ ] `resource_id: str = Field(..., description="ULID primary identifier")` - Required, no default - - [ ] `name: str = Field(..., min_length=1, max_length=100, description="Human-readable resource name")` - - [ ] `type: ResourceType = Field(..., description="Type of resource determining available operations")` - - [ ] `location: str = Field(..., description="Path, URL, or connection string")` - - [ ] `is_remote: bool = Field(default=False, description="Whether resource is network-accessible")` - - [ ] `sandbox_strategy: SandboxStrategy = Field(..., description="How to sandbox this resource during execution")` - - [ ] `read_only: bool = Field(default=False, description="If True, write operations are blocked")` - - [ ] `metadata: dict[str, Any] = Field(default_factory=dict, description="Additional type-specific metadata")` - - [ ] `created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation timestamp")` - - [ ] Commit: "feat(domain): define Resource model fields" - - [ ] **B1.2c** [Hamza] Add validators: - - [ ] `@field_validator('resource_id')` - Validate ULID format (26 alphanumeric chars) - - [ ] `@field_validator('location')` - Validate based on type (path for filesystem, URL for git remote, etc.) - - [ ] `@field_validator('sandbox_strategy')` - Warn if incompatible with resource type (e.g., GIT_WORKTREE on FILESYSTEM) - - [ ] Add `@model_validator(mode='after')` to check sandbox_strategy is compatible with type - - [ ] Commit: "feat(domain): add Resource model validators" - - [ ] **B1.2d** [Hamza] Add computed properties and helper methods: - - [ ] `@property def supports_sandbox(self) -> bool:` - Returns True if sandbox_strategy != NONE - - [ ] `@property def can_write(self) -> bool:` - Returns True if not read_only - - [ ] `def get_sandbox_path(self, base_dir: str) -> str:` - Generate sandbox path for this resource - - [ ] Commit: "feat(domain): add Resource helper methods" - - [ ] **B1.5** [Hamza] Define `ValidationConfig` Pydantic model in `src/cleveragents/domain/models/core/project.py`: - - [ ] **B1.5a** [Hamza] Create file with ValidationConfig model: - - [ ] Import necessary Pydantic types - - [ ] Create `class ValidationConfig(BaseModel):` - - [ ] Commit: "feat(domain): create project.py with ValidationConfig scaffold" - - [ ] **B1.5b** [Hamza] Define all fields: - - [ ] `test_command: str | None = Field(default=None, description="Shell command to run tests (e.g., 'pytest')")` - - [ ] `lint_command: str | None = Field(default=None, description="Shell command to run linter (e.g., 'ruff check .')")` - - [ ] `type_check_command: str | None = Field(default=None, description="Shell command for type checking (e.g., 'pyright')")` - - [ ] `build_command: str | None = Field(default=None, description="Shell command to build project (e.g., 'npm run build')")` - - [ ] `custom_commands: dict[str, str] = Field(default_factory=dict, description="Named custom validation commands")` - - [ ] `timeout_seconds: int = Field(default=300, description="Maximum time for each validation command")` - - [ ] `fail_on_lint_error: bool = Field(default=True, description="Whether lint errors should block apply")` - - [ ] Commit: "feat(domain): define ValidationConfig fields" - - [ ] **B1.5c** [Hamza] Add helper methods: - - [ ] `def get_all_commands(self) -> dict[str, str]:` - Returns all non-None commands as dict - - [ ] `def has_any_validation(self) -> bool:` - Returns True if any command is configured - - [ ] Commit: "feat(domain): add ValidationConfig helper methods" - - [ ] **B1.6** [Hamza] Define `ContextConfig` Pydantic model: - - [ ] **B1.6a** [Hamza] Define all fields: - - [ ] `ignore_patterns: list[str] = Field(default_factory=list, description="Gitignore-style patterns to exclude from indexing")` - - [ ] `include_patterns: list[str] | None = Field(default=None, description="If set, only files matching these patterns are included")` - - [ ] `max_file_size: int = Field(default=1_000_000, description="Maximum file size in bytes to index (default 1MB)")` - - [ ] `max_files: int = Field(default=100_000, description="Maximum number of files to index")` - - [ ] `indexing_strategy: str = Field(default="full_text", description="How to index: full_text, embeddings, or both")` - - [ ] `chunking_policy: str = Field(default="smart", description="How to chunk large files: fixed, semantic, or smart")` - - [ ] `chunk_size: int = Field(default=1000, description="Target chunk size in tokens for chunking")` - - [ ] Commit: "feat(domain): define ContextConfig fields" - - [ ] **B1.6b** [Hamza] Add default ignore patterns: - - [ ] `@field_validator('ignore_patterns', mode='before')` - Merge with defaults if not explicitly empty - - [ ] Default patterns: `[".git/", "node_modules/", "__pycache__/", ".venv/", "*.pyc", ".DS_Store"]` - - [ ] Commit: "feat(domain): add ContextConfig default ignore patterns" - - [ ] **B1.1** [Hamza] Define `Project` Pydantic model in `src/cleveragents/domain/models/core/project.py`: - - [ ] **B1.1a** [Hamza] Import Resource model and create Project class: - - [ ] Import `Resource` from resource module - - [ ] Import `ValidationConfig`, `ContextConfig` from same file - - [ ] Create `class Project(BaseModel):` with proper config - - [ ] Commit: "feat(domain): add Project model scaffold" - - [ ] **B1.1b** [Hamza] Define identity fields: - - [ ] `project_id: str = Field(..., description="ULID primary identifier")` - - [ ] `name: str = Field(..., min_length=1, max_length=100, description="Project display name")` - - [ ] `namespace: str = Field(default="local", description="Namespace: local/, username/, orgname/")` - - [ ] `description: str | None = Field(default=None, max_length=500, description="Optional project description")` - - [ ] Commit: "feat(domain): define Project identity fields" - - [ ] **B1.1c** [Hamza] Define categorization and resource fields: - - [ ] `tags: list[str] = Field(default_factory=list, description="Categorization tags (e.g., 'python', 'backend')")` - - [ ] `resources: list[Resource] = Field(default_factory=list, description="Resources associated with this project")` - - [ ] `validation_config: ValidationConfig | None = Field(default=None, description="Project-level validation commands")` - - [ ] `context_config: ContextConfig = Field(default_factory=ContextConfig, description="Context indexing configuration")` - - [ ] Commit: "feat(domain): define Project categorization and resource fields" - - [ ] **B1.1d** [Hamza] Define timestamp fields: - - [ ] `created_at: datetime = Field(default_factory=datetime.utcnow)` - - [ ] `updated_at: datetime = Field(default_factory=datetime.utcnow)` - - [ ] Commit: "feat(domain): define Project timestamp fields" - - [ ] **B1.1e** [Hamza] Add computed property for is_remote: - - [ ] `@property def is_remote(self) -> bool:` - Returns True only if ALL resources have is_remote=True - - [ ] Empty resources list: return False (local by default) - - [ ] Mixed local/remote: return False (has local resources, so project is local) - - [ ] All remote: return True (can execute on server) - - [ ] Commit: "feat(domain): add Project.is_remote computed property" - - [ ] **B1.1f** [Hamza] Add namespace validator: - - [ ] `@field_validator('namespace')` - Validate namespace format - - [ ] Must match pattern: `^(local|[a-z][a-z0-9_]{0,49})$` (local or valid identifier) - - [ ] Reserved namespaces: `["openai", "anthropic", "google", "cleveragents"]` - reject these - - [ ] Commit: "feat(domain): add Project namespace validator" - - [ ] **B1.1g** [Hamza] Add namespaced_name property and helpers: - - [ ] `@property def namespaced_name(self) -> str:` - Returns f"{self.namespace}/{self.name}" - - [ ] `@classmethod def parse_namespaced_name(cls, full_name: str) -> tuple[str, str]:` - Split into (namespace, name) - - [ ] `def add_resource(self, resource: Resource) -> 'Project':` - Returns new project with resource added (immutable pattern) - - [ ] `def remove_resource(self, resource_id: str) -> 'Project':` - Returns new project without resource - - [ ] `def get_resource(self, name: str) -> Resource | None:` - Find resource by name - - [ ] Commit: "feat(domain): add Project helper methods" - - [ ] Tests: Behave scenarios for model validation - - [ ] **B1.7** [Rui] Write 25 Behave scenarios in `features/project_model.feature`: - - [ ] **B1.7a** [Rui] Project creation scenarios: - - [ ] Scenario: Create valid project with all required fields - - [ ] Scenario: Create project with optional description - - [ ] Scenario: Create project with multiple tags - - [ ] Scenario: Project creation fails with empty name - - [ ] Scenario: Project creation fails with name > 100 chars - - [ ] Commit: "test(behave): add project creation scenarios" - - [ ] **B1.7b** [Rui] Namespace validation scenarios: - - [ ] Scenario: Project namespace "local" is valid - - [ ] Scenario: Project namespace "myuser" is valid - - [ ] Scenario: Project namespace "my_org_name" is valid - - [ ] Scenario: Project namespace starting with number is invalid - - [ ] Scenario: Project namespace "openai" (reserved) is rejected - - [ ] Scenario: Project namespaced_name returns "namespace/name" format - - [ ] Commit: "test(behave): add namespace validation scenarios" - - [ ] **B1.7c** [Rui] is_remote derivation scenarios: - - [ ] Scenario: Project with no resources has is_remote=False - - [ ] Scenario: Project with one local resource has is_remote=False - - [ ] Scenario: Project with one remote resource has is_remote=True - - [ ] Scenario: Project with mixed local/remote resources has is_remote=False - - [ ] Scenario: Project with all remote resources has is_remote=True - - [ ] Commit: "test(behave): add is_remote derivation scenarios" - - [ ] **B1.7d** [Rui] Resource model scenarios: - - [ ] Scenario: Resource with each ResourceType value validates correctly - - [ ] Scenario: Resource with GIT_WORKTREE strategy on GIT_REPOSITORY is valid - - [ ] Scenario: Resource with COPY_ON_WRITE strategy on FILESYSTEM is valid - - [ ] Scenario: Resource with TRANSACTION_ROLLBACK on DATABASE is valid - - [ ] Scenario: Resource with read_only=True rejects write operations - - [ ] Scenario: Resource location validated based on type - - [ ] Commit: "test(behave): add resource model scenarios" - - [ ] **B1.7e** [Rui] ValidationConfig scenarios: - - [ ] Scenario: ValidationConfig with all commands validates - - [ ] Scenario: ValidationConfig with only test_command validates - - [ ] Scenario: ValidationConfig get_all_commands returns non-None commands - - [ ] Scenario: ValidationConfig custom_commands are included - - [ ] Commit: "test(behave): add ValidationConfig scenarios" - - [ ] **B1.7f** [Rui] ContextConfig scenarios: - - [ ] Scenario: ContextConfig ignore patterns accept glob syntax - - [ ] Scenario: ContextConfig default ignore patterns applied - - [ ] Scenario: ContextConfig max_file_size enforced - - [ ] Commit: "test(behave): add ContextConfig scenarios" - - [ ] **B1.7g** [Rui] Serialization scenarios: - - [ ] Scenario: Project JSON serialization round-trips correctly - - [ ] Scenario: Resource JSON serialization preserves enum values - - [ ] Scenario: Project with nested resources serializes completely - - [ ] Commit: "test(behave): add serialization round-trip scenarios" - -- [ ] **Stage B2: Project CLI Commands** (Day 3-4) **[Hamza]** - - **SEQUENTIAL ORDER**: B2.1 (File scaffold) → B2.2 (Create) → B2.3 (Add resource) → B2.4 (Remove resource) → B2.5 (List) → B2.6 (Show) → B2.7 (Validation) → B2.8 (Delete) → B2.9 (Register) - - - [ ] Code: Implement project CLI - - [ ] **B2.1** [Hamza] Create `src/cleveragents/cli/commands/project.py` scaffold: - - [ ] **B2.1a** [Hamza] Create file with imports and Click group: - - [ ] Import `click` for CLI framework - - [ ] Import `rich.console.Console`, `rich.table.Table` for output - - [ ] Import Project, Resource models from domain - - [ ] Import ProjectService from application.services - - [ ] Create `@click.group(name="project")` decorator - - [ ] Add docstring: "Manage projects and their resources" - - [ ] Commit: "feat(cli): create project.py with Click group scaffold" - - [ ] **B2.1b** [Hamza] Create ProjectService in `src/cleveragents/application/services/project_service.py`: - - [ ] Import ProjectRepository, ResourceRepository - - [ ] Define `class ProjectService:` - - [ ] Add `__init__(self, project_repo: ProjectRepository, resource_repo: ResourceRepository)` - - [ ] Add stub methods: `create_project()`, `get_project()`, `list_projects()`, `delete_project()` - - [ ] Commit: "feat(service): add ProjectService scaffold" - - [ ] **B2.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project create` command: - - [ ] **B2.2a** [Hamza] Define command signature: - ```python - @project.command("create") - @click.option("--name", "-n", required=True, help="Project name (namespace/name format)") - @click.option("--description", "-d", default=None, help="Project description") - @click.option("--tag", "-t", multiple=True, help="Project tags (can specify multiple)") - def create_project(name: str, description: str | None, tag: tuple[str, ...]): - ``` - - [ ] Commit: "feat(cli): add project create command signature" - - [ ] **B2.2b** [Hamza] Implement namespace parsing: - - [ ] Split name on "/" to get (namespace, short_name) - - [ ] If no "/" present, default namespace to "local" - - [ ] Validate namespace: must match `^(local|[a-z][a-z0-9_]{0,49})$` - - [ ] Validate short_name: 1-100 chars, alphanumeric + hyphens - - [ ] Raise `click.BadParameter` on validation failure - - [ ] Commit: "feat(cli): implement namespace parsing in project create" - - [ ] **B2.2c** [Hamza] Implement project creation: - - [ ] Generate ULID for project_id: `ulid.new().str` - - [ ] Create `Project` domain model with all fields - - [ ] Call `project_service.create_project(project)` - - [ ] Handle `DuplicateProjectError` - display user-friendly message - - [ ] Commit: "feat(cli): implement project creation logic" - - [ ] **B2.2d** [Hamza] Implement success output: - - [ ] Display: "Created project: {namespace}/{short_name}" - - [ ] Display: "Project ID: {project_id}" - - [ ] Display: "Tags: {tags}" if any - - [ ] Use Rich console for colored output (green for success) - - [ ] Commit: "feat(cli): add project create success output" - - [ ] **B2.2e** [Hamza] Add ProjectService.create_project() implementation: - - [ ] Validate project.name is unique via repository - - [ ] If duplicate, raise `DuplicateProjectError(name=project.name)` - - [ ] Persist via `self._project_repo.create(project)` - - [ ] Return created project - - [ ] Commit: "feat(service): implement ProjectService.create_project()" - - [ ] **B2.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project add-resource` command: - - [ ] **B2.3a** [Hamza] Define command signature: - ```python - @project.command("add-resource") - @click.option("--project", "-p", required=True, help="Project name (namespace/name)") - @click.option("--name", "-n", required=True, help="Resource name") - @click.option("--type", "-t", "resource_type", required=True, - type=click.Choice(["git_repository", "filesystem", "database", "api_endpoint"])) - @click.option("--location", "-l", required=True, help="Path, URL, or connection string") - @click.option("--sandbox-strategy", "-s", required=True, - type=click.Choice(["git_worktree", "copy_on_write", "transaction_rollback", "none"])) - @click.option("--read-only", is_flag=True, help="Mark resource as read-only") - @click.option("--metadata", "-m", multiple=True, help="Key=value metadata pairs") - ``` - - [ ] Commit: "feat(cli): add project add-resource command signature" - - [ ] **B2.3b** [Hamza] Implement resource type validation: - - [ ] Map CLI type string to ResourceType enum - - [ ] Validate sandbox strategy is compatible with resource type: - - [ ] git_repository: allows git_worktree, copy_on_write, none - - [ ] filesystem: allows copy_on_write, overlay, none - - [ ] database: allows transaction_rollback, none - - [ ] api_endpoint: only allows none - - [ ] Raise `click.BadParameter` if incompatible - - [ ] Commit: "feat(cli): validate resource type and sandbox strategy compatibility" - - [ ] **B2.3c** [Hamza] Implement location validation: - - [ ] For git_repository: validate path exists or URL is valid git URL - - [ ] For filesystem: validate path exists and is directory - - [ ] For database: validate connection string format (basic check) - - [ ] For api_endpoint: validate URL format - - [ ] Commit: "feat(cli): validate resource location by type" - - [ ] **B2.3d** [Hamza] Implement metadata parsing: - - [ ] Parse each `--metadata` value as "key=value" - - [ ] Build dict from all pairs - - [ ] Handle missing "=" gracefully (error) - - [ ] Commit: "feat(cli): parse metadata key=value pairs" - - [ ] **B2.3e** [Hamza] Implement resource creation and linking: - - [ ] Fetch project by namespaced name - - [ ] Raise `ProjectNotFoundError` if not exists - - [ ] Check resource name is unique within project - - [ ] Create Resource with ULID and all fields - - [ ] Add resource to project: `project_service.add_resource(project_id, resource)` - - [ ] Display success: "Added resource '{name}' to project '{project_name}'" - - [ ] Commit: "feat(cli): implement add-resource creation and linking" - - [ ] **B2.3f** [Hamza] Add ProjectService.add_resource() implementation: - - [ ] Fetch project from repository - - [ ] Check for duplicate resource name in project - - [ ] Create new project with resource added (immutable pattern) - - [ ] Recompute project.is_remote based on all resources - - [ ] Update project in repository - - [ ] Create resource in resource repository - - [ ] Return updated project - - [ ] Commit: "feat(service): implement ProjectService.add_resource()" - - [ ] **B2.4** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project remove-resource` command: - - [ ] **B2.4a** [Hamza] Define command signature: - ```python - @project.command("remove-resource") - @click.option("--project", "-p", required=True, help="Project name") - @click.option("--name", "-n", required=True, help="Resource name to remove") - @click.option("--yes", is_flag=True, help="Skip confirmation") - ``` - - [ ] Commit: "feat(cli): add project remove-resource command signature" - - [ ] **B2.4b** [Hamza] Implement removal logic: - - [ ] Fetch project and validate resource exists - - [ ] If not --yes, prompt for confirmation: "Remove resource '{name}'? [y/N]" - - [ ] Call `project_service.remove_resource(project_id, resource_name)` - - [ ] Display success: "Removed resource '{name}' from project" - - [ ] Commit: "feat(cli): implement remove-resource logic" - - [ ] **B2.4c** [Hamza] Add ProjectService.remove_resource() implementation: - - [ ] Fetch project - - [ ] Find resource by name - - [ ] Raise `ResourceNotFoundError` if not exists - - [ ] Create new project without resource (immutable pattern) - - [ ] Recompute is_remote - - [ ] Update project - - [ ] Delete resource from resource repository - - [ ] Return updated project - - [ ] Commit: "feat(service): implement ProjectService.remove_resource()" - - [ ] **B2.5** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project list` command: - - [ ] **B2.5a** [Hamza] Define command signature: - ```python - @project.command("list") - @click.option("--namespace", "-n", default=None, help="Filter by namespace") - @click.option("--tag", "-t", default=None, help="Filter by tag") - @click.option("--format", "output_format", type=click.Choice(["table", "json"]), default="table") - ``` - - [ ] Commit: "feat(cli): add project list command signature" - - [ ] **B2.5b** [Hamza] Implement query and filtering: - - [ ] Call `project_service.list_projects(namespace=namespace, tag=tag)` - - [ ] If namespace provided, filter by namespace - - [ ] If tag provided, filter projects that have this tag - - [ ] Commit: "feat(cli): implement project list filtering" - - [ ] **B2.5c** [Hamza] Implement table output: - - [ ] Create Rich Table with columns: ID, Name, Resources, Tags, Remote - - [ ] Add row for each project: - - [ ] ID: first 8 chars of project_id - - [ ] Name: namespaced_name - - [ ] Resources: count of resources - - [ ] Tags: comma-separated tags (truncate if >3) - - [ ] Remote: "Yes" or "No" based on is_remote - - [ ] Display table via console.print() - - [ ] If no projects found, display "No projects found" - - [ ] Commit: "feat(cli): implement project list table output" - - [ ] **B2.5d** [Hamza] Implement JSON output: - - [ ] If format=json, serialize projects to JSON - - [ ] Use model_dump_json() for each project - - [ ] Print to stdout (for piping to jq, etc.) - - [ ] Commit: "feat(cli): implement project list JSON output" - - [ ] **B2.5e** [Hamza] Add ProjectService.list_projects() implementation: - - [ ] Call `project_repo.list_all()` - - [ ] Apply namespace filter if provided - - [ ] Apply tag filter if provided - - [ ] Return filtered list - - [ ] Commit: "feat(service): implement ProjectService.list_projects()" - - [ ] **B2.6** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project show` command: - - [ ] **B2.6a** [Hamza] Define command signature: - ```python - @project.command("show") - @click.argument("name") - @click.option("--format", "output_format", type=click.Choice(["rich", "json"]), default="rich") - ``` - - [ ] Commit: "feat(cli): add project show command signature" - - [ ] **B2.6b** [Hamza] Implement project fetch and rich display: - - [ ] Fetch project by namespaced name via service - - [ ] If not found, display error and exit(1) - - [ ] Display project details using Rich panels: - ``` - ╭─ Project: local/my-project ────────────────────────╮ - │ ID: 01ARZ3NDEKTSV4RRFFQ69G5FAV │ - │ Description: My awesome project │ - │ Tags: python, backend │ - │ Remote: No │ - │ Created: 2024-01-15 10:30:00 │ - ╰───────────────────────────────────────────────────╯ - - Resources (2): - ┌─────────────────┬──────────────────┬─────────────────┬──────────┐ - │ Name │ Type │ Location │ Strategy │ - ├─────────────────┼──────────────────┼─────────────────┼──────────┤ - │ source │ git_repository │ /path/to/repo │ worktree │ - │ config │ filesystem │ /path/to/config │ copy │ - └─────────────────┴──────────────────┴─────────────────┴──────────┘ - - Validation Config: - Test: pytest tests/ - Lint: ruff check src/ - Type Check: pyright src/ - ``` - - [ ] Commit: "feat(cli): implement project show rich display" - - [ ] **B2.6c** [Hamza] Implement JSON output: - - [ ] If format=json, output full project as JSON - - [ ] Include all resources and validation config - - [ ] Commit: "feat(cli): implement project show JSON output" - - [ ] **B2.6d** [Hamza] Add ProjectService.get_project() implementation: - - [ ] Parse namespaced name to (namespace, short_name) - - [ ] Query by namespaced_name OR by project_id (support both) - - [ ] Return Project with resources loaded - - [ ] Commit: "feat(service): implement ProjectService.get_project()" - - [ ] **B2.7** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project set-validation` command: - - [ ] **B2.7a** [Hamza] Define command signature: - ```python - @project.command("set-validation") - @click.option("--project", "-p", required=True, help="Project name") - @click.option("--test-command", default=None, help="Command to run tests") - @click.option("--lint-command", default=None, help="Command to run linter") - @click.option("--type-check-command", default=None, help="Command for type checking") - @click.option("--build-command", default=None, help="Command to build project") - @click.option("--timeout", default=300, type=int, help="Timeout for each command (seconds)") - @click.option("--clear", is_flag=True, help="Clear all validation config") - ``` - - [ ] Commit: "feat(cli): add project set-validation command signature" - - [ ] **B2.7b** [Hamza] Implement validation config update: - - [ ] Fetch project - - [ ] If --clear, set validation_config to None - - [ ] Otherwise, create ValidationConfig with provided commands - - [ ] Only set commands that were explicitly provided (preserve existing if not specified) - - [ ] Call `project_service.update_validation(project_id, config)` - - [ ] Display updated config summary - - [ ] Commit: "feat(cli): implement set-validation logic" - - [ ] **B2.7c** [Hamza] Add ProjectService.update_validation() implementation: - - [ ] Fetch project - - [ ] Merge new config with existing (if not --clear) - - [ ] Update project with new validation_config - - [ ] Persist via repository - - [ ] Commit: "feat(service): implement ProjectService.update_validation()" - - [ ] **B2.8** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project delete` command: - - [ ] **B2.8a** [Hamza] Define command signature: - ```python - @project.command("delete") - @click.argument("name") - @click.option("--force", "-f", is_flag=True, help="Force delete even if plans exist") - @click.option("--yes", is_flag=True, help="Skip confirmation") - ``` - - [ ] Commit: "feat(cli): add project delete command signature" - - [ ] **B2.8b** [Hamza] Implement deletion checks: - - [ ] Fetch project - - [ ] Check for active plans using this project: `plan_repo.count(project_id=project.project_id)` - - [ ] If plans exist and not --force: - - [ ] Display error: "Cannot delete project with {n} active plans. Use --force to delete anyway." - - [ ] List plan IDs (first 5) - - [ ] Exit with code 1 - - [ ] If --force, display warning: "Deleting project with {n} active plans" - - [ ] Commit: "feat(cli): implement project delete safety checks" - - [ ] **B2.8c** [Hamza] Implement deletion: - - [ ] Prompt for confirmation: "Delete project '{name}'? This cannot be undone. [y/N]" - - [ ] Support `--yes` to bypass confirmation - - [ ] If confirmed (or --yes), call `project_service.delete_project(project_id)` - - [ ] Display success: "Deleted project '{name}'" - - [ ] Commit: "feat(cli): implement project delete confirmation and execution" - - [ ] **B2.8d** [Hamza] Add ProjectService.delete_project() implementation: - - [ ] Delete all resources for project via resource_repo - - [ ] Delete project via project_repo - - [ ] Return True on success - - [ ] Commit: "feat(service): implement ProjectService.delete_project()" - - [ ] **B2.9** [Hamza] Register project commands in `src/cleveragents/cli/main.py`: - - [ ] **B2.9a** [Hamza] Import and register: - - [ ] Add `from cleveragents.cli.commands.project import project as project_group` - - [ ] Add `app.add_command(project_group)` in main app setup - - [ ] Verify `agents [--data-dir PATH] [--config-path PATH] project --help` shows all subcommands - - [ ] Commit: "feat(cli): register project commands in main CLI" - - [ ] **B2.9b** [Hamza] Add DI wiring for ProjectService: - - [ ] Update container.py to provide ProjectService - - [ ] Inject into CLI commands via Click context or similar pattern - - [ ] Commit: "feat(di): wire ProjectService into CLI" - - [ ] Tests: Behave + Robot for all project CLI commands - - [ ] **B2.10** [Rui] Write Behave scenarios in `features/project_cli.feature`: - - [ ] **B2.10a** [Rui] Project creation scenarios: - - [ ] Scenario: Create project with valid name succeeds - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project create --name local/test-project` - - [ ] Then the output contains "Created project: local/test-project" - - [ ] And the output contains "Project ID:" - - [ ] Scenario: Create project with description and tags - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project create --name local/test --description "My project" --tag python --tag backend` - - [ ] Then the project has description "My project" - - [ ] And the project has tags "python", "backend" - - [ ] Scenario: Create project with duplicate name fails - - [ ] Given a project "local/existing" exists - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project create --name local/existing` - - [ ] Then the exit code is 1 - - [ ] And the output contains "already exists" - - [ ] Scenario: Create project with invalid namespace fails - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project create --name 123invalid/test` - - [ ] Then the exit code is 1 - - [ ] And the output contains "Invalid namespace" - - [ ] Commit: "test(behave): add project create CLI scenarios" - - [ ] **B2.10b** [Rui] Add resource scenarios: - - [ ] Scenario: Add git repository resource to project - - [ ] Given a project "local/test" exists - - [ ] And a git repository exists at "/tmp/test-repo" - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project add-resource --project local/test --name source --type git_repository --location /tmp/test-repo --sandbox-strategy git_worktree` - - [ ] Then the output contains "Added resource 'source'" - - [ ] Scenario: Add filesystem resource to project - - [ ] Given a project "local/test" exists - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project add-resource --project local/test --name config --type filesystem --location /tmp/config --sandbox-strategy copy_on_write` - - [ ] Then the output contains "Added resource 'config'" - - [ ] Scenario: Add resource with incompatible sandbox strategy fails - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project add-resource --project local/test --name api --type api_endpoint --location https://api.example.com --sandbox-strategy git_worktree` - - [ ] Then the exit code is 1 - - [ ] And the output contains "incompatible" - - [ ] Scenario: Add resource with metadata - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project add-resource ... --metadata branch=main --metadata remote=origin` - - [ ] Then the resource has metadata key "branch" with value "main" - - [ ] Commit: "test(behave): add resource CLI scenarios" - - [ ] **B2.10c** [Rui] Remove resource scenarios: - - [ ] Scenario: Remove resource from project succeeds - - [ ] Given project "local/test" has resource "source" - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project remove-resource --project local/test --name source --yes` - - [ ] Then the output contains "Removed resource 'source'" - - [ ] Scenario: Remove non-existent resource fails gracefully - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project remove-resource --project local/test --name nonexistent --yes` - - [ ] Then the exit code is 1 - - [ ] And the output contains "not found" - - [ ] Commit: "test(behave): add remove-resource CLI scenarios" - - [ ] **B2.10d** [Rui] List and show scenarios: - - [ ] Scenario: List projects shows all projects - - [ ] Given projects "local/proj1" and "local/proj2" exist - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project list` - - [ ] Then the output contains "proj1" - - [ ] And the output contains "proj2" - - [ ] Scenario: List projects with namespace filter works - - [ ] Given projects "local/proj1" and "team/proj2" exist - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project list --namespace local` - - [ ] Then the output contains "proj1" - - [ ] And the output does not contain "proj2" - - [ ] Scenario: List projects JSON format works - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project list --format json` - - [ ] Then the output is valid JSON - - [ ] Scenario: Show project displays full details - - [ ] Given project "local/test" with 2 resources exists - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project show local/test` - - [ ] Then the output contains "local/test" - - [ ] And the output contains "Resources (2)" - - [ ] Commit: "test(behave): add list and show CLI scenarios" - - [ ] **B2.10e** [Rui] Validation config scenarios: - - [ ] Scenario: Set validation commands persists correctly - - [ ] Given project "local/test" exists - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project set-validation --project local/test --test-command "pytest" --lint-command "ruff check"` - - [ ] Then project "local/test" has test_command "pytest" - - [ ] And project "local/test" has lint_command "ruff check" - - [ ] Scenario: Clear validation config works - - [ ] Given project "local/test" has validation config - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project set-validation --project local/test --clear` - - [ ] Then project "local/test" has no validation config - - [ ] Commit: "test(behave): add validation config CLI scenarios" - - [ ] **B2.10f** [Rui] Delete scenarios: - - [ ] Scenario: Delete project succeeds - - [ ] Given project "local/test" exists with no plans - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project delete local/test --yes` - - [ ] Then the output contains "Deleted project" - - [ ] And project "local/test" no longer exists - - [ ] Scenario: Delete project with active plans blocked without --force - - [ ] Given project "local/test" exists - - [ ] And a plan uses project "local/test" - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project delete local/test --yes` - - [ ] Then the exit code is 1 - - [ ] And the output contains "active plans" - - [ ] Scenario: Delete project with active plans succeeds with --force - - [ ] Given project "local/test" exists with active plans - - [ ] When I run `agents [--data-dir PATH] [--config-path PATH] project delete local/test --force --yes` - - [ ] Then the output contains "Deleted project" - - [ ] Commit: "test(behave): add delete CLI scenarios" - - [ ] **B2.11** [Rui] Write Robot integration test `robot/project_cli_integration.robot`: - - [ ] **B2.11a** [Rui] Full lifecycle test: - - [ ] Test: Full project lifecycle - - [ ] Create project with description and tags - - [ ] Add git repository resource - - [ ] Add filesystem resource - - [ ] Show project and verify all details - - [ ] Set validation commands - - [ ] List projects and verify presence - - [ ] Remove one resource - - [ ] Delete project - - [ ] Verify project no longer exists - - [ ] Commit: "test(robot): add full project lifecycle e2e test" - - [ ] **B2.11b** [Rui] Multi-resource test: - - [ ] Test: Project with multiple resources of different types - - [ ] Create project - - [ ] Add git repo resource (primary code) - - [ ] Add filesystem resource (docs) - - [ ] Add database resource (read-only) - - [ ] Verify is_remote is computed correctly (should be False - has local resources) - - [ ] Show project and verify all resources listed - - [ ] Commit: "test(robot): add multi-resource project e2e test" - -- [ ] **Stage B3: Sandbox Framework** (Day 3-5) **[Luis + Hamza - Architectural, CRITICAL PATH]** - - **PARALLEL SUBTRACKS**: - - TRACK B3.protocol [Luis - Day 3 AM]: Protocol + Status + Factory (B3.1, B3.2, B3.5, B3.6) - - TRACK B3.git [Hamza - Day 3 PM - Day 4]: Git Worktree Implementation (B3.3) - - TRACK B3.fs [Hamza - Day 4]: Filesystem Implementation (B3.4) - - TRACK B3.manager [Luis - Day 4-5]: Manager + Merge (B3.7, B3.8) - - TRACK B3.tests [Rui - Day 3-5]: Tests in parallel with implementation - - - [ ] Code: Implement sandbox abstraction - - [ ] **B3.1** [Luis] Define `Sandbox` protocol in `src/cleveragents/infrastructure/sandbox/protocol.py`: - - [ ] **B3.1a** [Luis] Create file with necessary imports: - - [ ] Import `Protocol, runtime_checkable` from typing - - [ ] Import `ABC, abstractmethod` from abc - - [ ] Import `dataclasses` for result types - - [ ] Add module docstring explaining sandbox abstraction purpose - - [ ] Commit: "feat(sandbox): create protocol.py with imports" - - [ ] **B3.1b** [Luis] Define `SandboxContext` dataclass: - - [ ] `sandbox_id: str` - Unique identifier (ULID) for this sandbox instance - - [ ] `sandbox_path: str` - Root path where sandboxed files live - - [ ] `original_path: str` - Original resource location - - [ ] `resource_id: str` - ID of resource being sandboxed - - [ ] `plan_id: str` - ID of plan that created this sandbox - - [ ] `created_at: datetime` - When sandbox was created - - [ ] `metadata: dict[str, Any]` - Implementation-specific data (e.g., git branch name) - - [ ] Commit: "feat(sandbox): define SandboxContext dataclass" - - [ ] **B3.1c** [Luis] Define `CommitResult` dataclass: - - [ ] `sandbox_id: str` - Which sandbox was committed - - [ ] `success: bool` - Whether commit succeeded - - [ ] `commit_ref: str | None` - Git commit hash or equivalent reference - - [ ] `changed_files: list[str]` - List of files that were changed - - [ ] `added_files: list[str]` - List of files that were created - - [ ] `deleted_files: list[str]` - List of files that were removed - - [ ] `error: str | None` - Error message if success=False - - [ ] `timestamp: datetime` - When commit occurred - - [ ] Commit: "feat(sandbox): define CommitResult dataclass" - - [ ] **B3.1d** [Luis] Define `Sandbox` protocol: - ```python - @runtime_checkable - class Sandbox(Protocol): - """Protocol for resource sandboxing implementations.""" - - @property - def sandbox_id(self) -> str: - """Unique identifier for this sandbox.""" - ... - - @property - def resource(self) -> Resource: - """The resource being sandboxed.""" - ... - - @property - def status(self) -> SandboxStatus: - """Current status of the sandbox.""" - ... - - @property - def context(self) -> SandboxContext | None: - """Context after sandbox is created, None before.""" - ... - - def create(self, plan_id: str) -> SandboxContext: - """Initialize sandbox environment. Returns context with paths.""" - ... - - def get_path(self, resource_path: str) -> str: - """Translate resource-relative path to sandbox absolute path.""" - ... - - def commit(self, message: str | None = None) -> CommitResult: - """Finalize sandbox changes. Returns result with changed files.""" - ... - - def rollback(self) -> None: - """Discard all sandbox changes. Sandbox can still be used.""" - ... - - def cleanup(self) -> None: - """Remove sandbox artifacts. Sandbox cannot be used after this.""" - ... - ``` - - [ ] Commit: "feat(sandbox): define Sandbox protocol" - - [ ] **B3.2** [Luis] Define `SandboxStatus` enum in same file: - - [ ] **B3.2a** [Luis] Define status values with docstrings: - - [ ] `PENDING = "pending"` - Sandbox created but not yet initialized - - [ ] `CREATED = "created"` - Sandbox initialized, ready for use - - [ ] `ACTIVE = "active"` - Sandbox has been written to - - [ ] `COMMITTED = "committed"` - Changes have been applied to original - - [ ] `ROLLED_BACK = "rolled_back"` - Changes have been discarded - - [ ] `CLEANED_UP = "cleaned_up"` - Sandbox artifacts removed, terminal state - - [ ] `ERRORED = "errored"` - Sandbox operation failed - - [ ] Commit: "feat(sandbox): define SandboxStatus enum" - - [ ] **B3.2b** [Luis] Add status transition validation: - - [ ] `@classmethod def valid_transitions(cls) -> dict[SandboxStatus, list[SandboxStatus]]:` - Define allowed transitions - - [ ] PENDING → CREATED, ERRORED - - [ ] CREATED → ACTIVE, COMMITTED (no changes), CLEANED_UP - - [ ] ACTIVE → COMMITTED, ROLLED_BACK, ERRORED - - [ ] COMMITTED → CLEANED_UP - - [ ] ROLLED_BACK → ACTIVE (retry), CLEANED_UP - - [ ] ERRORED → CLEANED_UP - - [ ] CLEANED_UP → (terminal, no transitions) - - [ ] Commit: "feat(sandbox): add SandboxStatus transition validation" - - [ ] **B3.3** [Hamza] Implement `GitWorktreeSandbox` in `src/cleveragents/infrastructure/sandbox/git_worktree.py`: - - [ ] **B3.3a** [Hamza] Create class scaffold with constructor: - - [ ] Import subprocess for git commands - - [ ] Import logging, tempfile, shutil, os - - [ ] Import protocol types from protocol.py - - [ ] Define `class GitWorktreeSandbox:` implementing Sandbox protocol - - [ ] Constructor: `__init__(self, resource: Resource)`: - - [ ] Validate `resource.type == ResourceType.GIT_REPOSITORY` - - [ ] Validate `resource.sandbox_strategy == SandboxStrategy.GIT_WORKTREE` - - [ ] Store resource reference - - [ ] Initialize `_sandbox_id = ulid.new().str` - - [ ] Initialize `_status = SandboxStatus.PENDING` - - [ ] Initialize `_context: SandboxContext | None = None` - - [ ] Initialize `_worktree_path: str | None = None` - - [ ] Initialize `_branch_name: str | None = None` - - [ ] Commit: "feat(sandbox): add GitWorktreeSandbox class scaffold" - - [ ] **B3.3b** [Hamza] Implement `create(plan_id: str) -> SandboxContext`: - - [ ] Validate status is PENDING - - [ ] Generate unique branch name: `f"cleveragents-sandbox-{self._sandbox_id}"` - - [ ] Generate worktree path: `tempfile.mkdtemp(prefix=f"ca_git_worktree_{plan_id}_")` - - [ ] Determine repo root from resource.location (handle both path and URL) - - [ ] If resource is remote URL, first clone to temp location - - [ ] Run git command: `git worktree add {worktree_path} -b {branch_name}` - - [ ] Handle errors: if worktree fails, cleanup and raise - - [ ] Store paths in instance variables - - [ ] Create and store SandboxContext - - [ ] Update status to CREATED - - [ ] Log: "Created git worktree sandbox {sandbox_id} at {worktree_path}" - - [ ] Return context - - [ ] Commit: "feat(sandbox): implement GitWorktreeSandbox.create()" - - [ ] **B3.3c** [Hamza] Implement helper method for git commands: - - [ ] `def _run_git(self, args: list[str], cwd: str | None = None) -> subprocess.CompletedProcess`: - - [ ] Build full command: `["git"] + args` - - [ ] Run with `subprocess.run(capture_output=True, text=True, check=False)` - - [ ] Log command and output at DEBUG level - - [ ] If returncode != 0, log error at WARNING level - - [ ] Return CompletedProcess for caller to handle - - [ ] Commit: "feat(sandbox): add GitWorktreeSandbox._run_git() helper" - - [ ] **B3.3d** [Hamza] Implement `get_path(resource_path: str) -> str`: - - [ ] Validate sandbox is CREATED or ACTIVE - - [ ] Validate resource_path does not escape sandbox (no `..` traversal) - - [ ] Return `os.path.join(self._worktree_path, resource_path)` - - [ ] Commit: "feat(sandbox): implement GitWorktreeSandbox.get_path()" - - [ ] **B3.3e** [Hamza] Implement `commit(message: str | None = None) -> CommitResult`: - - [ ] Validate status is CREATED or ACTIVE - - [ ] Default message: `f"CleverAgents sandbox commit [{self._sandbox_id}]"` - - [ ] Run `git status --porcelain` to check for changes - - [ ] If no changes, return CommitResult(success=True, changed_files=[]) - - [ ] Run `git add -A` to stage all changes - - [ ] Parse `git diff --cached --name-status` to get changed/added/deleted lists - - [ ] Run `git commit -m "{message}"` to commit - - [ ] Get commit hash with `git rev-parse HEAD` - - [ ] Update status to COMMITTED - - [ ] Return CommitResult with all fields populated - - [ ] Commit: "feat(sandbox): implement GitWorktreeSandbox.commit()" - - [ ] **B3.3f** [Hamza] Implement `rollback() -> None`: - - [ ] Validate status is CREATED or ACTIVE - - [ ] Run `git checkout .` to discard modified files - - [ ] Run `git clean -fd` to remove untracked files - - [ ] Run `git reset HEAD` to unstage any staged changes - - [ ] Update status to ROLLED_BACK - - [ ] Log: "Rolled back git worktree sandbox {sandbox_id}" - - [ ] Commit: "feat(sandbox): implement GitWorktreeSandbox.rollback()" - - [ ] **B3.3g** [Hamza] Implement `cleanup() -> None`: - - [ ] Can be called from any non-terminal status - - [ ] Run `git worktree remove {worktree_path} --force` from repo root - - [ ] If worktree remove fails, try `shutil.rmtree(self._worktree_path)` as fallback - - [ ] Optionally delete the sandbox branch: `git branch -D {branch_name}` - - [ ] Update status to CLEANED_UP - - [ ] Clear context and paths - - [ ] Log: "Cleaned up git worktree sandbox {sandbox_id}" - - [ ] Commit: "feat(sandbox): implement GitWorktreeSandbox.cleanup()" - - [ ] **B3.3h** [Hamza] Add proper error handling: - - [ ] Define `SandboxError` base exception in protocol.py - - [ ] Define `SandboxCreationError(SandboxError)` - failed to create - - [ ] Define `SandboxCommitError(SandboxError)` - failed to commit - - [ ] Define `SandboxRollbackError(SandboxError)` - failed to rollback - - [ ] All methods should catch subprocess errors and wrap in SandboxError - - [ ] On error, update status to ERRORED and include original exception - - [ ] Commit: "feat(sandbox): add GitWorktreeSandbox error handling" - - [ ] **B3.4** [Hamza] Implement `FilesystemSandbox` in `src/cleveragents/infrastructure/sandbox/filesystem.py`: - - [ ] **B3.4a** [Hamza] Create class scaffold: - - [ ] Similar structure to GitWorktreeSandbox - - [ ] Constructor validates `resource.type == ResourceType.FILESYSTEM` - - [ ] Constructor validates `resource.sandbox_strategy == SandboxStrategy.COPY_ON_WRITE` - - [ ] Commit: "feat(sandbox): add FilesystemSandbox class scaffold" - - [ ] **B3.4b** [Hamza] Implement `create(plan_id: str) -> SandboxContext`: - - [ ] Generate sandbox path: `tempfile.mkdtemp(prefix=f"ca_fs_sandbox_{plan_id}_")` - - [ ] Copy resource directory to sandbox: `shutil.copytree(resource.location, sandbox_path, dirs_exist_ok=True)` - - [ ] Use `shutil.ignore_patterns()` to skip .git, node_modules, __pycache__ - - [ ] Record file hashes of original for diff detection later - - [ ] Create and store SandboxContext - - [ ] Update status to CREATED - - [ ] Commit: "feat(sandbox): implement FilesystemSandbox.create()" - - [ ] **B3.4c** [Hamza] Implement `get_path(resource_path: str) -> str`: - - [ ] Same pattern as GitWorktreeSandbox - - [ ] Commit: "feat(sandbox): implement FilesystemSandbox.get_path()" - - [ ] **B3.4d** [Hamza] Implement `commit(message: str | None = None) -> CommitResult`: - - [ ] Compare sandbox files with original (using recorded hashes) - - [ ] Build lists of changed/added/deleted files - - [ ] For each changed file: `shutil.copy2(sandbox_file, original_file)` - - [ ] For each new file: copy and create parent dirs as needed - - [ ] For each deleted file: `os.remove(original_file)` - - [ ] Use atomic operations where possible (write to .tmp then rename) - - [ ] Update status to COMMITTED - - [ ] Return CommitResult with file lists - - [ ] Commit: "feat(sandbox): implement FilesystemSandbox.commit()" - - [ ] **B3.4e** [Hamza] Implement `rollback() -> None`: - - [ ] For filesystem, rollback is implicit - just don't commit - - [ ] Re-copy original to sandbox to reset: `shutil.rmtree(sandbox); shutil.copytree(original, sandbox)` - - [ ] Update status to ROLLED_BACK - - [ ] Commit: "feat(sandbox): implement FilesystemSandbox.rollback()" - - [ ] **B3.4f** [Hamza] Implement `cleanup() -> None`: - - [ ] Remove sandbox directory: `shutil.rmtree(self._sandbox_path, ignore_errors=True)` - - [ ] Update status to CLEANED_UP - - [ ] Commit: "feat(sandbox): implement FilesystemSandbox.cleanup()" - - [ ] **B3.5** [Luis] Implement `NoSandbox` in `src/cleveragents/infrastructure/sandbox/no_sandbox.py`: - - [ ] **B3.5a** [Luis] Create class for non-sandboxable resources: - - [ ] For APIs, some cloud resources, etc. - - [ ] Constructor: accept any Resource with sandbox_strategy=NONE - - [ ] Commit: "feat(sandbox): add NoSandbox class scaffold" - - [ ] **B3.5b** [Luis] Implement all methods as passthrough or warnings: - - [ ] `create()`: Log WARNING "Resource {name} is not sandboxed - changes are immediate" - - [ ] `get_path(resource_path)`: Return original resource path unchanged - - [ ] `commit()`: Return CommitResult(success=True) - changes already applied - - [ ] `rollback()`: Log ERROR "Rollback not possible for non-sandboxed resource" - - [ ] `cleanup()`: No-op, status to CLEANED_UP - - [ ] Commit: "feat(sandbox): implement NoSandbox methods" - - [ ] **B3.6** [Luis] Implement `SandboxFactory` in `src/cleveragents/infrastructure/sandbox/factory.py`: - - [ ] **B3.6a** [Luis] Create factory class: - - [ ] Import all sandbox implementations - - [ ] Define `class SandboxFactory:` - - [ ] Commit: "feat(sandbox): add SandboxFactory scaffold" - - [ ] **B3.6b** [Luis] Implement `create_sandbox(resource: Resource) -> Sandbox`: - - [ ] Match on `resource.sandbox_strategy`: - ```python - match resource.sandbox_strategy: - case SandboxStrategy.GIT_WORKTREE: - return GitWorktreeSandbox(resource) - case SandboxStrategy.COPY_ON_WRITE: - return FilesystemSandbox(resource) - case SandboxStrategy.OVERLAY: - # Overlay not implemented yet, fall back to copy - logger.warning("Overlay not implemented, using copy-on-write") - return FilesystemSandbox(resource) - case SandboxStrategy.TRANSACTION_ROLLBACK: - raise NotImplementedError("Database sandboxing not yet implemented") - case SandboxStrategy.NONE: - return NoSandbox(resource) - case _: - raise ValueError(f"Unknown sandbox strategy: {resource.sandbox_strategy}") - ``` - - [ ] Commit: "feat(sandbox): implement SandboxFactory.create_sandbox()" - - [ ] **B3.6c** [Luis] Add validation helper: - - [ ] `@staticmethod def is_supported(resource: Resource) -> bool:` - Check if sandboxing is supported - - [ ] `@staticmethod def get_supported_strategies(resource_type: ResourceType) -> list[SandboxStrategy]:` - Valid combos - - [ ] Commit: "feat(sandbox): add SandboxFactory validation helpers" - - [ ] **B3.7** [Luis] Implement sandbox lifecycle management: - - [ ] **B3.7a** [Luis] Create `SandboxManager` in `src/cleveragents/infrastructure/sandbox/manager.py`: - - [ ] Import threading for lock management - - [ ] Import factory and protocol types - - [ ] Define `class SandboxManager:` - - [ ] Instance variables: - - [ ] `_factory: SandboxFactory` - injected via constructor - - [ ] `_active_sandboxes: dict[str, dict[str, Sandbox]]` - plan_id -> resource_id -> Sandbox - - [ ] `_lock: threading.RLock` - thread safety for sandbox tracking - - [ ] `_cleanup_on_exit: bool` - whether to cleanup on process exit (default True) - - [ ] Commit: "feat(sandbox): add SandboxManager scaffold" - - [ ] **B3.7b** [Luis] Implement `get_or_create_sandbox(plan_id: str, resource: Resource) -> Sandbox`: - - [ ] Acquire lock - - [ ] Check if sandbox already exists for this plan+resource - - [ ] If exists and status is usable (CREATED, ACTIVE, ROLLED_BACK), return it - - [ ] If exists but cleaned up, remove from tracking - - [ ] Create new sandbox via factory - - [ ] Call sandbox.create(plan_id) to initialize - - [ ] Store in _active_sandboxes - - [ ] Release lock - - [ ] Return sandbox - - [ ] This is the LAZY sandboxing pattern - only create when needed - - [ ] Commit: "feat(sandbox): implement SandboxManager.get_or_create_sandbox()" - - [ ] **B3.7c** [Luis] Implement `commit_all(plan_id: str) -> list[CommitResult]`: - - [ ] Get all sandboxes for plan_id - - [ ] For each sandbox with status ACTIVE: - - [ ] Call sandbox.commit() - - [ ] Collect CommitResult - - [ ] Return list of all results - - [ ] If any commit fails, don't rollback others (partial commit possible, caller decides) - - [ ] Commit: "feat(sandbox): implement SandboxManager.commit_all()" - - [ ] **B3.7d** [Luis] Implement `rollback_all(plan_id: str) -> None`: - - [ ] Get all sandboxes for plan_id - - [ ] For each sandbox with status ACTIVE: - - [ ] Call sandbox.rollback() - - [ ] Log any rollback failures but continue with others - - [ ] Commit: "feat(sandbox): implement SandboxManager.rollback_all()" - - [ ] **B3.7e** [Luis] Implement `cleanup_all(plan_id: str) -> None`: - - [ ] Get all sandboxes for plan_id - - [ ] For each sandbox: - - [ ] Call sandbox.cleanup() - - [ ] Remove plan_id entry from _active_sandboxes - - [ ] Commit: "feat(sandbox): implement SandboxManager.cleanup_all()" - - [ ] **B3.7f** [Luis] Implement `cleanup_abandoned() -> int`: - - [ ] Find sandbox directories matching pattern that aren't tracked - - [ ] Check if creating process is still alive (via PID file or lock) - - [ ] If process dead, clean up the directory - - [ ] Return count of cleaned sandboxes - - [ ] This is called on application startup - - [ ] Commit: "feat(sandbox): implement SandboxManager.cleanup_abandoned()" - - [ ] **B3.7g** [Luis] Add atexit handler for graceful cleanup: - - [ ] Register `atexit.register(self._cleanup_on_exit_handler)` - - [ ] Handler calls cleanup_all for all tracked plans - - [ ] Commit: "feat(sandbox): add SandboxManager atexit cleanup" - - [ ] **B3.8** [Luis] Implement merge strategies in `src/cleveragents/infrastructure/sandbox/merge.py`: - - [ ] **B3.8a** [Luis] Define merge types and protocol: - - [ ] Define `MergeResult` dataclass: - - [ ] `success: bool` - - [ ] `content: str | bytes` - merged content - - [ ] `has_conflicts: bool` - - [ ] `conflict_markers: list[tuple[int, int]]` - line ranges with conflicts - - [ ] Define `MergeStrategy(Protocol)`: - - [ ] Method `merge(base: str, ours: str, theirs: str) -> MergeResult` - - [ ] Commit: "feat(sandbox): define merge types and protocol" - - [ ] **B3.8b** [Luis] Implement `GitMergeStrategy`: - - [ ] Use `git merge-file` for three-way merge - - [ ] Write base, ours, theirs to temp files - - [ ] Run `git merge-file -p ours base theirs` - - [ ] Parse output for conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) - - [ ] Return MergeResult with content and conflict info - - [ ] Commit: "feat(sandbox): implement GitMergeStrategy" - - [ ] **B3.8c** [Luis] Implement `SequentialMergeStrategy`: - - [ ] For non-mergeable resources, apply changes in order - - [ ] "Theirs" (second change) always wins - - [ ] Return MergeResult(success=True, content=theirs) - - [ ] Commit: "feat(sandbox): implement SequentialMergeStrategy" - - [ ] **B3.8d** [Luis] Implement `JsonMergeStrategy`: - - [ ] Parse both as JSON - - [ ] Deep merge objects (recursive dict merge) - - [ ] Arrays: concatenate or last-wins based on config - - [ ] Return serialized merged JSON - - [ ] Commit: "feat(sandbox): implement JsonMergeStrategy" - - [ ] Tests: Integration tests for each sandbox type - - [ ] **B3.9** [Rui] Write Behave scenarios in `features/sandbox_git_worktree.feature`: - - [ ] **B3.9a** [Rui] Creation scenarios: - - [ ] Scenario: Create git worktree sandbox from local git repo - - [ ] Given a git repository at "/tmp/test-repo" with files - - [ ] When I create a GitWorktreeSandbox for that resource - - [ ] And I call sandbox.create("plan-123") - - [ ] Then a worktree exists at the sandbox path - - [ ] And the sandbox status is CREATED - - [ ] And the original repo is unchanged - - [ ] Scenario: Create sandbox from remote git URL (if applicable) - - [ ] Scenario: Create fails for non-git resource type - - [ ] Commit: "test(behave): add git worktree creation scenarios" - - [ ] **B3.9b** [Rui] Modification isolation scenarios: - - [ ] Scenario: Modify file in sandbox does not affect original - - [ ] Given a created git worktree sandbox - - [ ] When I write "modified content" to sandbox path "test.py" - - [ ] Then the original repo's "test.py" is unchanged - - [ ] And the sandbox status is ACTIVE - - [ ] Scenario: Create new file in sandbox does not appear in original - - [ ] Scenario: Delete file in sandbox does not delete original - - [ ] Commit: "test(behave): add git worktree isolation scenarios" - - [ ] **B3.9c** [Rui] Commit scenarios: - - [ ] Scenario: Commit sandbox creates git commit - - [ ] Given a sandbox with modified files - - [ ] When I call sandbox.commit("Test commit") - - [ ] Then a git commit exists with message "Test commit" - - [ ] And CommitResult.changed_files contains "test.py" - - [ ] And sandbox status is COMMITTED - - [ ] Scenario: Commit with no changes succeeds with empty file list - - [ ] Scenario: Commit includes all staged and unstaged changes - - [ ] Commit: "test(behave): add git worktree commit scenarios" - - [ ] **B3.9d** [Rui] Rollback and cleanup scenarios: - - [ ] Scenario: Rollback sandbox discards all changes - - [ ] Given a sandbox with modified files - - [ ] When I call sandbox.rollback() - - [ ] Then the sandbox files match original - - [ ] And sandbox status is ROLLED_BACK - - [ ] Scenario: Cleanup removes worktree and branch - - [ ] Scenario: Multiple sandboxes from same repo are isolated - - [ ] Commit: "test(behave): add git worktree rollback/cleanup scenarios" - - [ ] **B3.10** [Rui] Write Behave scenarios in `features/sandbox_filesystem.feature`: - - [ ] Similar structure to B3.9 but for filesystem sandbox - - [ ] Scenario: Create filesystem sandbox copies directory - - [ ] Scenario: Large directory copy uses efficient patterns - - [ ] Scenario: Ignore patterns (node_modules, .git) are skipped during copy - - [ ] Scenario: Modify file in sandbox does not affect original - - [ ] Scenario: Commit sandbox applies changes to original atomically - - [ ] Scenario: Rollback sandbox resets to original state - - [ ] Scenario: Cleanup removes temp directory - - [ ] Commit: "test(behave): add filesystem sandbox scenarios" - - [ ] **B3.11** [Rui] Write Robot integration test `robot/sandbox_integration.robot`: - - [ ] **B3.11a** [Rui] Full lifecycle test: - - [ ] Create real git repo with multiple files - - [ ] Create sandbox, modify files, commit - - [ ] Verify changes appear in repo - - [ ] Create second sandbox, rollback, verify no changes - - [ ] Cleanup, verify temp directories removed - - [ ] Commit: "test(robot): add full sandbox lifecycle e2e test" - - [ ] **B3.11b** [Rui] Parallel sandbox test: - - [ ] Create two sandboxes on same repo - - [ ] Modify different files in each - - [ ] Commit both - - [ ] Verify no cross-contamination - - [ ] Commit: "test(robot): add parallel sandbox isolation e2e test" - - [ ] Tests: Parallel execution isolation tests - - [ ] **B3.12** [Rui] Write Behave scenarios in `features/sandbox_isolation.feature`: - - [ ] Scenario: Two plans with sandboxes on same resource are isolated - - [ ] Given Plan A creates sandbox for resource R - - [ ] And Plan B creates sandbox for same resource R - - [ ] When Plan A writes "A content" to file.txt - - [ ] And Plan B writes "B content" to file.txt - - [ ] Then Plan A's sandbox shows "A content" - - [ ] And Plan B's sandbox shows "B content" - - [ ] And original file is unchanged - - [ ] Scenario: Plan A cannot see Plan B's intermediate changes - - [ ] Scenario: Commits from different plans require merge - - [ ] Commit: "test(behave): add sandbox isolation scenarios" - - [ ] Tests: Merge conflict resolution tests - - [ ] **B3.13** [Rui] Write Behave scenarios in `features/sandbox_merge.feature`: - - [ ] Scenario: Git merge strategy handles non-conflicting changes - - [ ] Given base content "line1\nline2\nline3" - - [ ] And ours changes line1 to "modified1" - - [ ] And theirs changes line3 to "modified3" - - [ ] When I merge with GitMergeStrategy - - [ ] Then result is "modified1\nline2\nmodified3" - - [ ] And has_conflicts is False - - [ ] Scenario: Git merge strategy marks conflicts appropriately - - [ ] Given both ours and theirs change line2 - - [ ] When I merge - - [ ] Then has_conflicts is True - - [ ] And content contains conflict markers - - [ ] Scenario: Sequential merge uses theirs content - - [ ] Scenario: JSON merge combines object properties - - [ ] Commit: "test(behave): add merge strategy scenarios" - -- [ ] **Stage B4: Resource Integration** (Day 6-7) **[Hamza]** - - **SEQUENTIAL ORDER**: B4.1 (Types) → B4.2 (Service scaffold) → B4.3 (Access) → B4.4 (Lazy sandbox) → B4.5 (Commit/Rollback) → B4.6 (Cleanup hooks) → B4.7 (Lifecycle integration) - - - [ ] Code: Connect resources to plan execution - - [ ] **B4.1** [Hamza] Define resource access types in `src/cleveragents/domain/models/core/resource_access.py`: - - [ ] **B4.1a** [Hamza] Define `AccessMode` enum: - - [ ] `READ = "read"` - Read-only access, may use original or sandbox - - [ ] `WRITE = "write"` - Write access, requires sandbox - - [ ] `EXECUTE = "execute"` - Execute commands in context - - [ ] Commit: "feat(domain): define AccessMode enum" - - [ ] **B4.1b** [Hamza] Define `ResourceAccess` dataclass: - - [ ] `resource_id: str` - Which resource is being accessed - - [ ] `plan_id: str` - Which plan is accessing - - [ ] `mode: AccessMode` - How resource is being accessed - - [ ] `sandbox: Sandbox | None` - Sandbox if write mode - - [ ] `effective_path: str` - Resolved path (sandbox or original) - - [ ] `accessed_at: datetime` - When access was granted - - [ ] `is_sandboxed: bool` - Whether using sandbox or original - - [ ] Commit: "feat(domain): define ResourceAccess dataclass" - - [ ] **B4.1c** [Hamza] Define `ResourceAccessTracker` dataclass: - - [ ] `plan_id: str` - Plan being tracked - - [ ] `accesses: dict[str, ResourceAccess]` - resource_id -> access - - [ ] `read_resources: set[str]` - Resources accessed for read - - [ ] `write_resources: set[str]` - Resources accessed for write - - [ ] `first_write_at: datetime | None` - When first write occurred - - [ ] Commit: "feat(domain): define ResourceAccessTracker" - - [ ] **B4.2** [Hamza] Create `ResourceService` scaffold in `src/cleveragents/application/services/resource_service.py`: - - [ ] **B4.2a** [Hamza] Define class with dependencies: - ```python - class ResourceService: - def __init__( - self, - sandbox_manager: SandboxManager, - project_repo: ProjectRepository, - resource_repo: ResourceRepository, - config: ResourceServiceConfig - ): - self._sandbox_manager = sandbox_manager - self._project_repo = project_repo - self._resource_repo = resource_repo - self._config = config - self._trackers: dict[str, ResourceAccessTracker] = {} # plan_id -> tracker - self._lock = threading.RLock() - ``` - - [ ] Commit: "feat(service): add ResourceService scaffold with dependencies" - - [ ] **B4.2b** [Hamza] Define `ResourceServiceConfig` in `src/cleveragents/config/settings.py`: - - [ ] `force_sandbox_for_reads: bool = False` - Always sandbox, even for reads - - [ ] `preserve_sandbox_on_failure: bool = True` - Keep sandbox for debugging on error - - [ ] `auto_cleanup_abandoned: bool = True` - Cleanup orphaned sandboxes on startup - - [ ] `max_sandboxes_per_plan: int = 10` - Limit sandboxes per plan - - [ ] Commit: "feat(config): add ResourceServiceConfig" - - [ ] **B4.3** [Hamza] Implement `access_resource()` method: - - [ ] **B4.3a** [Hamza] Core method signature: - ```python - def access_resource( - self, - plan_id: str, - resource: Resource, - mode: AccessMode = AccessMode.READ - ) -> ResourceAccess: - ``` - - [ ] Commit: "feat(service): add access_resource() signature" - - [ ] **B4.3b** [Hamza] Implement tracker initialization: - - [ ] Acquire lock - - [ ] If no tracker for plan_id, create one - - [ ] Check if resource already accessed - - [ ] If already accessed with same or higher mode, return existing access - - [ ] Commit: "feat(service): implement access_resource() tracker init" - - [ ] **B4.3c** [Hamza] Implement read access logic: - - [ ] If mode is READ and not force_sandbox_for_reads: - - [ ] Return access with effective_path = resource.location - - [ ] Set is_sandboxed = False - - [ ] Record in tracker's read_resources - - [ ] Commit: "feat(service): implement read access without sandbox" - - [ ] **B4.3d** [Hamza] Implement write access logic: - - [ ] If mode is WRITE: - - [ ] Call `sandbox_manager.get_or_create_sandbox(plan_id, resource)` - - [ ] Get sandbox.context.sandbox_path - - [ ] Set effective_path = sandbox_path - - [ ] Set is_sandboxed = True - - [ ] Record in tracker's write_resources - - [ ] Set first_write_at if not set - - [ ] Commit: "feat(service): implement write access with sandbox" - - [ ] **B4.3e** [Hamza] Implement access upgrade: - - [ ] If resource was accessed as READ but now needs WRITE: - - [ ] Create sandbox if not exists - - [ ] Update tracker to reflect write mode - - [ ] Return new ResourceAccess with sandboxed path - - [ ] Commit: "feat(service): implement access mode upgrade" - - [ ] **B4.4** [Hamza] Implement lazy sandboxing pattern: - - [ ] **B4.4a** [Hamza] Sandbox created only when write occurs: - - [ ] `access_resource(plan_id, resource, READ)` - no sandbox - - [ ] First `access_resource(plan_id, resource, WRITE)` - creates sandbox - - [ ] Subsequent writes to same resource - reuses existing sandbox - - [ ] Log: "Created sandbox for resource {name} on first write" - - [ ] Commit: "feat(service): implement lazy sandbox creation" - - [ ] **B4.4b** [Hamza] Track sandbox lifecycle per plan: - - [ ] Method `get_plan_sandboxes(plan_id: str) -> list[Sandbox]`: - - [ ] Return all sandboxes associated with plan - - [ ] Method `has_pending_changes(plan_id: str) -> bool`: - - [ ] Check if any sandbox has uncommitted changes - - [ ] Commit: "feat(service): add sandbox tracking methods" - - [ ] **B4.5** [Hamza] Implement commit and rollback methods: - - [ ] **B4.5a** [Hamza] Implement `commit_plan_resources()`: - ```python - def commit_plan_resources(self, plan_id: str, message: str | None = None) -> list[CommitResult]: - """Commit all sandbox changes for a plan.""" - results = [] - sandboxes = self._sandbox_manager.get_sandboxes(plan_id) - for sandbox in sandboxes: - if sandbox.status == SandboxStatus.ACTIVE: - result = sandbox.commit(message or f"CleverAgents plan {plan_id}") - results.append(result) - self._log_commit_result(result) - return results - ``` - - [ ] Commit: "feat(service): implement commit_plan_resources()" - - [ ] **B4.5b** [Hamza] Implement `rollback_plan_resources()`: - ```python - def rollback_plan_resources(self, plan_id: str) -> None: - """Rollback all sandbox changes for a plan.""" - sandboxes = self._sandbox_manager.get_sandboxes(plan_id) - for sandbox in sandboxes: - if sandbox.status in (SandboxStatus.CREATED, SandboxStatus.ACTIVE): - sandbox.rollback() - logger.info(f"Rolled back sandbox {sandbox.sandbox_id}") - ``` - - [ ] Commit: "feat(service): implement rollback_plan_resources()" - - [ ] **B4.5c** [Hamza] Implement `cleanup_plan_resources()`: - ```python - def cleanup_plan_resources(self, plan_id: str) -> None: - """Clean up all sandbox artifacts for a plan.""" - self._sandbox_manager.cleanup_all(plan_id) - # Remove tracker - with self._lock: - if plan_id in self._trackers: - del self._trackers[plan_id] - logger.info(f"Cleaned up all resources for plan {plan_id}") - ``` - - [ ] Commit: "feat(service): implement cleanup_plan_resources()" - - [ ] **B4.6** [Hamza] Add sandbox cleanup hooks: - - [ ] **B4.6a** [Hamza] Implement plan completion hook: - - [ ] Create `PlanCompletionHandler` that listens for plan state changes - - [ ] On transition to APPLIED: commit then cleanup - - [ ] On transition to CANCELLED: rollback then cleanup - - [ ] Commit: "feat(service): add plan completion cleanup hook" - - [ ] **B4.6b** [Hamza] Implement failure handling hook: - - [ ] On plan transition to ERRORED: - - [ ] If preserve_sandbox_on_failure: keep sandbox for debugging - - [ ] Log: "Sandbox preserved for debugging: {sandbox_path}" - - [ ] Otherwise: rollback and cleanup - - [ ] Commit: "feat(service): add failure handling hook" - - [ ] **B4.6c** [Hamza] Implement application exit hook: - - [ ] Register `atexit.register(self._cleanup_all_on_exit)` - - [ ] Handler iterates all active trackers - - [ ] Cleanup all sandboxes (don't commit - exit is unexpected) - - [ ] Log warning if any uncommitted changes lost - - [ ] Commit: "feat(service): add atexit cleanup hook" - - [ ] **B4.6d** [Hamza] Implement startup cleanup: - - [ ] Method `cleanup_abandoned_sandboxes() -> int`: - - [ ] Called on application startup - - [ ] Call `sandbox_manager.cleanup_abandoned()` - - [ ] Return count of cleaned sandboxes - - [ ] Log: "Cleaned up {n} abandoned sandboxes from previous session" - - [ ] Commit: "feat(service): add startup cleanup for abandoned sandboxes" - - [ ] **B4.7** [Hamza] Integrate with plan execution lifecycle: - - [ ] **B4.7a** [Hamza] Update `PlanLifecycleService.apply_plan()`: - - [ ] Before apply: verify all sandboxes have changes - - [ ] Call `resource_service.commit_plan_resources(plan_id)` - - [ ] If any commit fails, rollback all and raise - - [ ] On success: cleanup resources - - [ ] Commit: "feat(service): integrate ResourceService with apply_plan()" - - [ ] **B4.7b** [Hamza] Update `PlanLifecycleService.cancel_plan()`: - - [ ] Call `resource_service.rollback_plan_resources(plan_id)` - - [ ] Call `resource_service.cleanup_plan_resources(plan_id)` - - [ ] Commit: "feat(service): integrate ResourceService with cancel_plan()" - - [ ] **B4.7c** [Hamza] Add DI wiring for ResourceService: - - [ ] Update container.py to provide ResourceService - - [ ] Inject into PlanLifecycleService - - [ ] Commit: "feat(di): wire ResourceService into container" - - [ ] Tests: End-to-end tests for plan execution with sandboxed resources - - [ ] **B4.8** [Rui] Write Behave scenarios in `features/resource_service.feature`: - - [ ] **B4.8a** [Rui] Access mode scenarios: - - [ ] Scenario: First write access creates sandbox - - [ ] Given a plan "plan-123" and resource "repo" with sandbox_strategy=git_worktree - - [ ] When I call `resource_service.access_resource("plan-123", repo, WRITE)` - - [ ] Then a sandbox is created for the resource - - [ ] And the returned ResourceAccess.is_sandboxed is True - - [ ] And the effective_path points to the sandbox location - - [ ] Scenario: Read access without write uses original - - [ ] When I call `resource_service.access_resource("plan-123", repo, READ)` - - [ ] Then no sandbox is created - - [ ] And ResourceAccess.is_sandboxed is False - - [ ] And effective_path points to original resource.location - - [ ] Commit: "test(behave): add resource access mode scenarios" - - [ ] **B4.8b** [Rui] Sandbox reuse scenarios: - - [ ] Scenario: Multiple writes use same sandbox - - [ ] Given I accessed resource for WRITE once - - [ ] When I access the same resource for WRITE again - - [ ] Then the same sandbox is returned - - [ ] And only one sandbox exists for this plan+resource - - [ ] Scenario: Access upgrade from READ to WRITE creates sandbox - - [ ] Given I accessed resource for READ (no sandbox) - - [ ] When I access the same resource for WRITE - - [ ] Then a sandbox is created - - [ ] And the effective_path changes to sandbox path - - [ ] Commit: "test(behave): add sandbox reuse scenarios" - - [ ] **B4.8c** [Rui] Commit and rollback scenarios: - - [ ] Scenario: Plan completion commits and cleans up sandbox - - [ ] Given a plan with sandbox containing changes - - [ ] When the plan transitions to APPLIED - - [ ] Then commit_plan_resources is called - - [ ] And all changes are committed to the original resource - - [ ] And the sandbox is cleaned up - - [ ] Scenario: Plan failure rolls back sandbox - - [ ] Given a plan with sandbox containing changes - - [ ] When the plan transitions to ERRORED - - [ ] Then rollback_plan_resources is called (if not preserve_sandbox_on_failure) - - [ ] And no changes are committed - - [ ] Scenario: Plan failure preserves sandbox for debugging - - [ ] Given preserve_sandbox_on_failure=True - - [ ] When the plan transitions to ERRORED - - [ ] Then the sandbox is NOT cleaned up - - [ ] And a log message indicates sandbox location for debugging - - [ ] Commit: "test(behave): add commit/rollback scenarios" - - [ ] **B4.8d** [Rui] Cleanup scenarios: - - [ ] Scenario: Application exit cleans up all sandboxes - - [ ] Given multiple plans with active sandboxes - - [ ] When the application exits - - [ ] Then all sandbox directories are removed - - [ ] Scenario: Startup cleans up abandoned sandboxes - - [ ] Given orphaned sandbox directories from previous crash - - [ ] When the application starts - - [ ] Then abandoned sandboxes are cleaned up - - [ ] And a log message indicates how many were cleaned - - [ ] Commit: "test(behave): add cleanup scenarios" - - [ ] **B4.9** [Rui] Write Robot integration test `robot/resource_service_integration.robot`: - - [ ] **B4.9a** [Rui] Full lifecycle test: - - [ ] Test: Full plan execution with sandboxed git resource - - [ ] Create a real git repository with files - - [ ] Create a project with git resource - - [ ] Create a plan targeting the project - - [ ] Access resource for write (sandbox created) - - [ ] Modify files via sandbox path - - [ ] Complete plan (commit and cleanup) - - [ ] Verify changes appear in original git repo - - [ ] Verify sandbox directory is removed - - [ ] Commit: "test(robot): add full resource service e2e test" - - [ ] **B4.9b** [Rui] Multi-resource test: - - [ ] Test: Plan with multiple resources - - [ ] Create project with git repo + filesystem resources - - [ ] Access both for write - - [ ] Modify files in both sandboxes - - [ ] Commit all - - [ ] Verify both original resources have changes - - [ ] Commit: "test(robot): add multi-resource plan e2e test" - -- [ ] **Stage B5: Project Persistence** (Day 7-8) **[Hamza]** - - **SEQUENTIAL ORDER**: B5.1 (Projects migration) → B5.2 (Resources migration) → B5.3 (Project model) → B5.4 (Resource model) → B5.5 (ProjectRepository) → B5.6 (ResourceRepository) → B5.7 (Tests) - - - [ ] Code: Project/Resource database schema - - [ ] **B5.1** [Hamza] Create Alembic migration for `projects` table: - - [ ] **B5.1a** [Hamza] Generate migration file: - - [ ] Run `alembic revision --autogenerate -m "create_projects_table"` - - [ ] Commit: "chore(db): generate projects table migration" - - [ ] **B5.1b** [Hamza] Define schema: - ```python - def upgrade(): - op.create_table( - 'projects', - sa.Column('project_id', sa.Text(), nullable=False), - sa.Column('name', sa.Text(), nullable=False), - sa.Column('namespace', sa.Text(), nullable=False), - sa.Column('short_name', sa.Text(), nullable=False), - sa.Column('description', sa.Text(), nullable=True), - sa.Column('tags', sa.JSON(), nullable=False, server_default='[]'), - sa.Column('is_remote', sa.Boolean(), nullable=False, server_default='false'), - sa.Column('validation_config', sa.JSON(), nullable=True), - sa.Column('context_config', sa.JSON(), nullable=True), - sa.Column('created_at', sa.Text(), nullable=False), - sa.Column('updated_at', sa.Text(), nullable=False), - sa.PrimaryKeyConstraint('project_id') - ) - op.create_index('ix_projects_name', 'projects', ['name'], unique=True) - op.create_index('ix_projects_namespace', 'projects', ['namespace']) - op.create_index('ix_projects_namespace_short_name', 'projects', ['namespace', 'short_name'], unique=True) - ``` - - [ ] Commit: "feat(db): add projects table schema" - - [ ] **B5.1c** [Hamza] Add downgrade: - ```python - def downgrade(): - op.drop_index('ix_projects_namespace_short_name') - op.drop_index('ix_projects_namespace') - op.drop_index('ix_projects_name') - op.drop_table('projects') - ``` - - [ ] Commit: "feat(db): add projects table downgrade" - - [ ] **B5.2** [Hamza] Create Alembic migration for `resources` table: - - [ ] **B5.2a** [Hamza] Generate migration file: - - [ ] Run `alembic revision --autogenerate -m "create_resources_table"` - - [ ] Commit: "chore(db): generate resources table migration" - - [ ] **B5.2b** [Hamza] Define schema: - ```python - def upgrade(): - op.create_table( - 'resources', - sa.Column('resource_id', sa.Text(), nullable=False), - sa.Column('project_id', sa.Text(), nullable=False), - sa.Column('name', sa.Text(), nullable=False), - sa.Column('type', sa.Text(), nullable=False), - sa.Column('location', sa.Text(), nullable=False), - sa.Column('is_remote', sa.Boolean(), nullable=False, server_default='false'), - sa.Column('sandbox_strategy', sa.Text(), nullable=False), - sa.Column('read_only', sa.Boolean(), nullable=False, server_default='false'), - sa.Column('metadata', sa.JSON(), nullable=False, server_default='{}'), - sa.Column('created_at', sa.Text(), nullable=False), - sa.PrimaryKeyConstraint('resource_id'), - sa.ForeignKeyConstraint(['project_id'], ['projects.project_id'], ondelete='CASCADE') - ) - op.create_index('ix_resources_project_id', 'resources', ['project_id']) - op.create_unique_constraint('uq_resources_project_name', 'resources', ['project_id', 'name']) - ``` - - [ ] Commit: "feat(db): add resources table schema with FK to projects" - - [ ] **B5.2c** [Hamza] Add downgrade: - - [ ] Drop constraint, index, and table - - [ ] Commit: "feat(db): add resources table downgrade" - - [ ] **B5.3** [Hamza] Create `ProjectModel` in `src/cleveragents/infrastructure/database/models.py`: - - [ ] **B5.3a** [Hamza] Define SQLAlchemy model: - ```python - class ProjectModel(Base): - __tablename__ = 'projects' - - project_id = Column(Text, primary_key=True) - name = Column(Text, nullable=False, unique=True) - namespace = Column(Text, nullable=False, index=True) - short_name = Column(Text, nullable=False) - description = Column(Text, nullable=True) - tags = Column(JSON, nullable=False, default=list) - is_remote = Column(Boolean, nullable=False, default=False) - validation_config = Column(JSON, nullable=True) - context_config = Column(JSON, nullable=True) - created_at = Column(Text, nullable=False) - updated_at = Column(Text, nullable=False) - - # Relationship to resources - resources = relationship("ResourceModel", back_populates="project", cascade="all, delete-orphan") - ``` - - [ ] Commit: "feat(db): add ProjectModel SQLAlchemy class" - - [ ] **B5.3b** [Hamza] Add domain conversion methods: - ```python - def to_domain(self) -> Project: - return Project( - project_id=self.project_id, - name=self.name, - namespace=self.namespace, - description=self.description, - tags=self.tags or [], - resources=[r.to_domain() for r in self.resources], - validation_config=ValidationConfig(**self.validation_config) if self.validation_config else None, - context_config=ContextConfig(**self.context_config) if self.context_config else ContextConfig(), - created_at=datetime.fromisoformat(self.created_at), - updated_at=datetime.fromisoformat(self.updated_at), - ) - - @classmethod - def from_domain(cls, project: Project) -> "ProjectModel": - return cls( - project_id=project.project_id, - name=project.namespaced_name, - namespace=project.namespace, - short_name=project.name, - description=project.description, - tags=project.tags, - is_remote=project.is_remote, - validation_config=project.validation_config.model_dump() if project.validation_config else None, - context_config=project.context_config.model_dump(), - created_at=project.created_at.isoformat(), - updated_at=project.updated_at.isoformat(), - ) - ``` - - [ ] Commit: "feat(db): add ProjectModel domain conversion methods" - - [ ] **B5.4** [Hamza] Create `ResourceModel` in same file: - - [ ] **B5.4a** [Hamza] Define SQLAlchemy model: - ```python - class ResourceModel(Base): - __tablename__ = 'resources' - - resource_id = Column(Text, primary_key=True) - project_id = Column(Text, ForeignKey('projects.project_id', ondelete='CASCADE'), nullable=False) - name = Column(Text, nullable=False) - type = Column(Text, nullable=False) - location = Column(Text, nullable=False) - is_remote = Column(Boolean, nullable=False, default=False) - sandbox_strategy = Column(Text, nullable=False) - read_only = Column(Boolean, nullable=False, default=False) - metadata = Column(JSON, nullable=False, default=dict) - created_at = Column(Text, nullable=False) - - # Relationship back to project - project = relationship("ProjectModel", back_populates="resources") - - __table_args__ = ( - UniqueConstraint('project_id', 'name', name='uq_resources_project_name'), - ) - ``` - - [ ] Commit: "feat(db): add ResourceModel SQLAlchemy class" - - [ ] **B5.4b** [Hamza] Add domain conversion methods: - - [ ] `to_domain()` - Convert to Resource domain model - - [ ] `from_domain()` - Create from Resource domain model - - [ ] Handle enum conversions (ResourceType, SandboxStrategy) - - [ ] Commit: "feat(db): add ResourceModel domain conversion methods" - - [ ] **B5.5** [Hamza] Implement `ProjectRepository` in `src/cleveragents/infrastructure/database/repositories.py`: - - [ ] **B5.5a** [Hamza] Define class with session factory: - ```python - class ProjectRepository: - def __init__(self, session_factory: Callable[[], Session]): - self._session_factory = session_factory - ``` - - [ ] Commit: "feat(repo): add ProjectRepository scaffold" - - [ ] **B5.5b** [Hamza] Implement `create(project: Project) -> Project`: - - [ ] Create ProjectModel from domain - - [ ] Add to session - - [ ] Handle duplicate name: raise `DuplicateProjectError` - - [ ] Commit transaction - - [ ] Return created project - - [ ] Commit: "feat(repo): implement ProjectRepository.create()" - - [ ] **B5.5c** [Hamza] Implement `get_by_id(project_id: str) -> Project | None`: - - [ ] Query by primary key with eager load of resources - - [ ] Convert to domain or return None - - [ ] Commit: "feat(repo): implement ProjectRepository.get_by_id()" - - [ ] **B5.5d** [Hamza] Implement `get_by_name(name: str) -> Project | None`: - - [ ] Query by namespaced name (unique index) - - [ ] Eager load resources - - [ ] Convert to domain - - [ ] Commit: "feat(repo): implement ProjectRepository.get_by_name()" - - [ ] **B5.5e** [Hamza] Implement `get_with_resources(project_id: str) -> Project | None`: - - [ ] Same as get_by_id but ensures resources are loaded - - [ ] Use `options(joinedload(ProjectModel.resources))` - - [ ] Commit: "feat(repo): implement ProjectRepository.get_with_resources()" - - [ ] **B5.5f** [Hamza] Implement `list_all(namespace: str | None = None) -> list[Project]`: - - [ ] Query all projects - - [ ] Filter by namespace if provided - - [ ] Order by namespace ASC, short_name ASC - - [ ] Convert all to domain - - [ ] Commit: "feat(repo): implement ProjectRepository.list_all()" - - [ ] **B5.5g** [Hamza] Implement `update(project: Project) -> Project`: - - [ ] Fetch existing by project_id - - [ ] Update all fields from domain - - [ ] Update `updated_at` to now - - [ ] Commit and return - - [ ] Commit: "feat(repo): implement ProjectRepository.update()" - - [ ] **B5.5h** [Hamza] Implement `delete(project_id: str) -> bool`: - - [ ] Delete project (cascade deletes resources via FK) - - [ ] Return True if deleted - - [ ] Commit: "feat(repo): implement ProjectRepository.delete()" - - [ ] **B5.6** [Hamza] Implement `ResourceRepository` in same file: - - [ ] **B5.6a** [Hamza] Define class: - - [ ] Same pattern as ProjectRepository - - [ ] Commit: "feat(repo): add ResourceRepository scaffold" - - [ ] **B5.6b** [Hamza] Implement `create(resource: Resource, project_id: str) -> Resource`: - - [ ] Create ResourceModel with project_id link - - [ ] Handle duplicate name within project: raise `DuplicateResourceError` - - [ ] Commit: "feat(repo): implement ResourceRepository.create()" - - [ ] **B5.6c** [Hamza] Implement `get_by_project(project_id: str) -> list[Resource]`: - - [ ] Query all resources with given project_id - - [ ] Order by name ASC - - [ ] Commit: "feat(repo): implement ResourceRepository.get_by_project()" - - [ ] **B5.6d** [Hamza] Implement `get_by_name(project_id: str, name: str) -> Resource | None`: - - [ ] Query by unique (project_id, name) pair - - [ ] Commit: "feat(repo): implement ResourceRepository.get_by_name()" - - [ ] **B5.6e** [Hamza] Implement `delete(resource_id: str) -> bool`: - - [ ] Delete resource by ID - - [ ] Commit: "feat(repo): implement ResourceRepository.delete()" - - [ ] Tests: Integration tests for persistence - - [ ] **B5.7** [Rui] Write Behave scenarios in `features/project_persistence.feature`: - - [ ] **B5.7a** [Rui] Project persistence scenarios: - - [ ] Scenario: Create project persists to database - - [ ] Given no project "local/test" exists - - [ ] When I create project "local/test" via ProjectRepository - - [ ] Then querying by name returns the project - - [ ] And project_id is a valid ULID - - [ ] Scenario: Update project persists changes - - [ ] Given project "local/test" exists - - [ ] When I update description to "New description" - - [ ] Then re-querying shows updated description - - [ ] And updated_at has changed - - [ ] Commit: "test(behave): add project persistence scenarios" - - [ ] **B5.7b** [Rui] Resource persistence scenarios: - - [ ] Scenario: Add resource persists and links to project - - [ ] Given project "local/test" exists - - [ ] When I add resource "source" to project - - [ ] Then ResourceRepository.get_by_project() returns the resource - - [ ] And resource.project_id matches the project - - [ ] Scenario: Get project includes all resources - - [ ] Given project "local/test" with 3 resources - - [ ] When I call ProjectRepository.get_with_resources() - - [ ] Then project.resources has 3 items - - [ ] And each resource has correct fields - - [ ] Commit: "test(behave): add resource persistence scenarios" - - [ ] **B5.7c** [Rui] Cascade scenarios: - - [ ] Scenario: Delete project cascades to resources - - [ ] Given project "local/test" with 2 resources - - [ ] When I delete the project - - [ ] Then ResourceRepository.get_by_project() returns empty list - - [ ] And the resource records no longer exist in database - - [ ] Commit: "test(behave): add cascade delete scenarios" - - [ ] **B5.7d** [Rui] Uniqueness scenarios: - - [ ] Scenario: Duplicate project name raises error - - [ ] Given project "local/test" exists - - [ ] When I try to create another "local/test" - - [ ] Then DuplicateProjectError is raised - - [ ] Scenario: Duplicate resource name within project raises error - - [ ] Given project "local/test" has resource "source" - - [ ] When I try to add another resource named "source" - - [ ] Then DuplicateResourceError is raised - - [ ] Scenario: Same resource name in different projects is allowed - - [ ] Given project "local/proj1" has resource "source" - - [ ] When I add resource "source" to project "local/proj2" - - [ ] Then it succeeds without error - - [ ] Commit: "test(behave): add uniqueness constraint scenarios" - - [ ] Method `get_by_project(project_id: str) -> list[Resource]` - - [ ] **B5.5** [Hamza] Create database model classes: - - [ ] `ProjectModel(Base)` with `to_domain()` and `from_domain()` - - [ ] `ResourceModel(Base)` with `to_domain()` and `from_domain()` - - [ ] Tests: Integration tests for persistence - - [ ] **B5.6** [Rui] Write Behave scenarios in `features/project_persistence.feature`: - - [ ] Scenario: Create project persists to database - - [ ] Scenario: Add resource persists and links to project - - [ ] Scenario: Get project includes all resources - - [ ] Scenario: Delete project cascades to resources +**M2 MERGE GATE**: +- Register a git-checkout resource and link it to a project via CLI. +- Create a sandbox for the linked resource and verify isolation via tests. +- Project context commands and validation attachment visibility work and persist. +- `nox` passes with coverage >=97%. **M2 SUCCESS CRITERIA**: -- [ ] Can create a project with resources via CLI -- [ ] Git repository resources can be sandboxed with worktrees -- [ ] Filesystem resources can be sandboxed with copy-on-write -- [ ] Plan execution uses sandboxed resources -- [ ] Sandbox cleanup works on success and failure - +- Resource registry supports resource types, resources, and DAG links with persistence (tables + repositories). +- Projects can link/unlink resources with CLI commands for resource types/resources/projects (list/show/tree included). +- Validation attachments (via `agents validation attach/detach`) appear in `project show` outputs. +- Git-checkout sandbox isolates changes; copy_on_write strategy returns clear NotImplementedError for fs-directory (documented). +- Resource/project services are DI-wired and exercised by Behave + Robot suites. +- `nox` passes with coverage >=97% across resource/project suites. --- ### Section 5: Actors, Skills & Tool Execution [WORKSTREAM C - Aditya Lead] @@ -3301,54 +1778,56 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **PARALLEL SUBTRACK C0.registry [Luis]**: Tool registry persistence + repositories **PARALLEL SUBTRACK C0.cli [Rui]**: CLI commands for tools/validations **SEQUENTIAL MERGE NOTE**: C0.domain must land before C0.registry/cli; C0.registry before C3 context wiring. - - [ ] **COMMIT (Owner: Jeff | Group: C0.domain) - Commit message: "feat(tool): add tool and validation domain models"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add `Tool` model with namespaced name, description, source type, input/output JSON schema, and capability metadata (read/write/checkpointable). - - [ ] Code [Jeff]: Add `ResourceBinding` model with slot definitions and binding modes (context, static, parameter). - - [ ] Code [Jeff]: Add `Validation` model as Tool subtype with `mode`, `wraps`, and `transform` fields; enforce read-only constraints. - - [ ] Code [Jeff]: Add enums for ToolSource, ToolType (tool/validation), and ValidationMode. - - [ ] Docs [Jeff]: Add `docs/reference/tool_model.md` and `docs/reference/validation_model.md` with examples. - - [ ] Tests (Behave) [Rui]: Add `features/tool_model.feature` for schema validation, resource binding rules, and validation constraints. - - [ ] Tests (Robot) [Rui]: Add `robot/tool_model.robot` smoke tests for model creation. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_model_bench.py` for schema validation throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(tool): add tool and validation domain models"`. - - [ ] **COMMIT (Owner: Luis | Group: C0.registry) - Commit message: "feat(tool): add tool registry persistence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add DB tables for `tools`, `tool_bindings`, and `validation_attachments` with indexes on namespaced name and type. - - [ ] Code [Luis]: Implement ToolRepository + ValidationAttachmentRepository with list/show filters. - - [ ] Code [Luis]: Add ToolRegistryService for register/update/remove/list/show with name conflict checks. - - [ ] Docs [Luis]: Update `docs/reference/database_schema.md` with tool/validation tables. - - [ ] Tests (Behave) [Rui]: Add `features/tool_registry.feature` for register/update/remove and validation-only constraints. - - [ ] Tests (Robot) [Rui]: Add `robot/tool_registry.robot` for list/show smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_registry_bench.py` for registry list performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Luis]: `git commit -m "feat(tool): add tool registry persistence"`. - - [ ] **COMMIT (Owner: Jeff | Group: C0.binding) - Commit message: "feat(tool): add resource binding resolution"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Implement binding resolution for contextual, static, and parameter bindings with type compatibility checks. - - [ ] Code [Jeff]: Add resolution helpers for resource name/ULID lookup and project-scoped filtering. - - [ ] Docs [Jeff]: Add `docs/reference/tool_bindings.md` with resolution order and examples. - - [ ] Tests (Behave) [Rui]: Add binding resolution scenarios (context vs static vs parameter). - - [ ] Tests (Robot) [Rui]: Add Robot test resolving a bound resource by name. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/binding_resolution_bench.py` for resolution latency. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Jeff]: `git commit -m "feat(tool): add resource binding resolution"`. - - [ ] **COMMIT (Owner: Rui | Group: C0.cli) - Commit message: "feat(cli): add tool and validation commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Rui]: Implement `agents tool add/remove/list/show` with YAML config input and `--type` filter. - - [ ] Code [Rui]: Implement `agents validation add/attach/detach` commands and enforce validation-only name use. - - [ ] Docs [Rui]: Update CLI reference with tool/validation commands and output format. - - [ ] Tests (Behave) [Rui]: Add CLI scenarios for tool/validation registration and attachment. - - [ ] Tests (Robot) [Rui]: Add Robot CLI suites for tool and validation commands. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_cli_bench.py` for CLI parsing overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - - [ ] Commit [Rui]: `git commit -m "feat(cli): add tool and validation commands"`. +- [ ] **COMMIT (Owner: Jeff | Group: C0.domain) - Commit message: "feat(tool): add tool and validation domain models"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add `Tool` model in `src/cleveragents/domain/models/core/tool.py` with namespaced name, description, source type, input/output JSON schema, and capability metadata (read/write/checkpointable). + - [ ] Code [Jeff]: Add `ResourceBinding` model with slot definitions and binding modes (context, static, parameter), plus required/optional flags. + - [ ] Code [Jeff]: Add `Validation` model as Tool subtype with `mode`, `wraps`, and `transform` fields; enforce read-only constraints. + - [ ] Code [Jeff]: Add enums for ToolSource, ToolType (tool/validation), and ValidationMode. + - [ ] Docs [Jeff]: Add `docs/reference/tool_model.md` and `docs/reference/validation_model.md` with examples. + - [ ] Tests (Behave) [Rui]: Add `features/tool_model.feature` for schema validation, resource binding rules, and validation constraints. + - [ ] Tests (Robot) [Rui]: Add `robot/tool_model.robot` smoke tests for model creation. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_model_bench.py` for schema validation throughput. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(tool): add tool and validation domain models"`. +- [ ] **COMMIT (Owner: Luis | Group: C0.registry) - Commit message: "feat(tool): add tool registry persistence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add DB tables for `tools`, `tool_bindings`, and `validation_attachments` with indexes on namespaced name and type. + - [ ] Code [Luis]: Include validation attachment columns for resource_id, optional project/plan scope, args_json, and attachment_id ULID. + - [ ] Code [Luis]: Implement ToolRepository + ValidationAttachmentRepository with list/show filters. + - [ ] Code [Luis]: Add ToolRegistryService for register/update/remove/list/show with name conflict checks. + - [ ] Docs [Luis]: Update `docs/reference/database_schema.md` with tool/validation tables. + - [ ] Tests (Behave) [Rui]: Add `features/tool_registry.feature` for register/update/remove and validation-only constraints. + - [ ] Tests (Robot) [Rui]: Add `robot/tool_registry.robot` for list/show smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_registry_bench.py` for registry list performance. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(tool): add tool registry persistence"`. +- [ ] **COMMIT (Owner: Jeff | Group: C0.binding) - Commit message: "feat(tool): add resource binding resolution"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Implement binding resolution for contextual, static, and parameter bindings with type compatibility checks. + - [ ] Code [Jeff]: Add resolution helpers for resource name/ULID lookup and project-scoped filtering. + - [ ] Docs [Jeff]: Add `docs/reference/tool_bindings.md` with resolution order and examples. + - [ ] Tests (Behave) [Rui]: Add binding resolution scenarios (context vs static vs parameter). + - [ ] Tests (Robot) [Rui]: Add Robot test resolving a bound resource by name. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/binding_resolution_bench.py` for resolution latency. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(tool): add resource binding resolution"`. +- [ ] **COMMIT (Owner: Rui | Group: C0.cli) - Commit message: "feat(cli): add tool and validation commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Rui]: Implement `agents tool add/remove/list/show` with YAML config input and `--type` filter. + - [ ] Code [Rui]: Implement `agents validation add/attach/detach` commands and enforce validation-only name use. + - [ ] Code [Rui]: Support `validation attach --project/--plan` flags and store attachment args. + - [ ] Docs [Rui]: Update CLI reference with tool/validation commands and output format. + - [ ] Tests (Behave) [Rui]: Add CLI scenarios for tool/validation registration and attachment. + - [ ] Tests (Robot) [Rui]: Add Robot CLI suites for tool and validation commands. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_cli_bench.py` for CLI parsing overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "feat(cli): add tool and validation commands"`. **Parallel Group C1: Actor Schema & Examples [Aditya + Jeff]** (start Day 5; C2 depends on this) - [ ] **COMMIT (Owner: Aditya | Group: C1.schema) - Commit message: "feat(actor): add actor yaml schema models"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Aditya]: Add schema models (ActorType, NodeType, ContextView, ToolDefinition, RouteDefinition, ActorConfigSchema) with strict validation. - - [ ] Code [Aditya]: Add tool-node schema fields that reference Tool Registry names, including validation nodes. + - [ ] Code [Aditya]: Add schema models (ActorType, NodeType, ContextView, ToolDefinition, RouteDefinition, ActorConfigSchema) with strict validation in `src/cleveragents/actor/schema.py`. + - [ ] Code [Aditya]: Add tool-node schema fields that reference Tool Registry names, including validation nodes, and require input/output schema presence. - [ ] Code [Aditya]: Add YAML load/serialize helpers and schema version guard. - [ ] Docs [Aditya]: Add `docs/reference/actors_schema.md` with field definitions, tool node semantics, and graph constraints. - [ ] Tests (Behave) [Rui]: Add `features/actor_schema.feature` scenarios for validation and topology errors. @@ -3359,6 +1838,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Commit [Aditya]: `git commit -m "feat(actor): add actor yaml schema models"`. - [ ] **COMMIT (Owner: Aditya | Group: C1.examples) - Commit message: "docs(actor): add actor yaml examples"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Docs [Aditya]: Add `docs/reference/actors_examples.md` with strategist, executor, reviewer, tool-only, validation-node, and graph YAML examples. + - [ ] Docs [Aditya]: Store example YAML files under `examples/actors/` for automated tests. - [ ] Tests (Behave) [Rui]: Add `features/actor_examples.feature` to ensure all examples validate. - [ ] Tests (Robot) [Rui]: Add `robot/actor_examples.robot` to load each example. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_examples_load_bench.py` for YAML load throughput. @@ -3367,8 +1847,10 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Commit [Aditya]: `git commit -m "docs(actor): add actor yaml examples"`. **Parallel Group C2: Actor Loading & Compilation [Aditya + Jeff]** (depends on C1) + **PARALLEL SUBTRACK C2.legacy [Jeff]**: Remove v2 actor config compatibility (after C1.schema) + **SEQUENTIAL NOTE**: C2.legacy must land before C2.loader/C2.compiler to avoid dual-format support. - [ ] **COMMIT (Owner: Aditya | Group: C2.loader) - Commit message: "feat(actor): add actor registry and loader"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Aditya]: Implement actor loader/registry with namespaced lookup and cache invalidation. + - [ ] Code [Aditya]: Implement actor loader/registry with namespaced lookup, cache invalidation, and file discovery in `actors/` and `examples/actors/`. - [ ] Code [Aditya]: Add registry integration with Tool Registry so tool nodes resolve at load time. - [ ] Docs [Aditya]: Add `docs/reference/actors_loading.md` with discovery rules and namespaces. - [ ] Tests (Behave) [Rui]: Add `features/actor_loading.feature` for discovery, duplicates, and namespace lookup. @@ -3398,10 +1880,22 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(actor): resolve actor references and subgraphs"`. +- [ ] **COMMIT (Owner: Jeff | Group: C2.legacy) - Commit message: "refactor(actor): drop v2 actor config compatibility"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Remove v2 JSON/YAML parsing paths in `src/cleveragents/actor/config.py` and related template engine usage. + - [ ] Code [Jeff]: Ensure only v3 actor YAML schema is accepted; provide clear error message when v2 fields are present. + - [ ] Docs [Jeff]: Update `docs/reference/actors_loading.md` with v3-only note and migration guidance. + - [ ] Tests (Behave) [Rui]: Add scenarios that reject v2 actor config files. + - [ ] Tests (Robot) [Rui]: Add Robot tests that attempt to load v2 configs and assert failure. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_schema_reject_bench.py` for validation overhead. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "refactor(actor): drop v2 actor config compatibility"`. + **Parallel Group C3: Skill Protocol & Context [Jeff]** (critical path; depends on C1) - [ ] **COMMIT (Owner: Jeff | Group: C3.protocol) - Commit message: "feat(skill): add skill protocol and metadata"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Define Skill protocol interface, SkillMetadata, SkillResult, and SkillError types. + - [ ] Code [Jeff]: Define Skill protocol interface, SkillMetadata, SkillResult, and SkillError types in `src/cleveragents/skills/protocol.py`. - [ ] Code [Jeff]: Add `SkillDefinition` model that references Tool Registry names and optional inline tool definitions. + - [ ] Code [Jeff]: Add error mapping helpers to normalize tool failures into SkillError payloads. - [ ] Docs [Jeff]: Add `docs/reference/skills_protocol.md` describing metadata, tool composition, and JSON schema rules. - [ ] Tests (Behave) [Rui]: Add `features/skill_protocol.feature` for metadata validation and error capture. - [ ] Tests (Robot) [Rui]: Add `robot/skill_protocol.robot` smoke tests. @@ -3410,8 +1904,9 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(skill): add skill protocol and metadata"`. - [ ] **COMMIT (Owner: Jeff | Group: C3.context) - Commit message: "feat(skill): add skill context and registry"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement SkillContext (plan/resource access, sandbox path, change tracker) and SkillRegistry. + - [ ] Code [Jeff]: Implement SkillContext (plan/resource access, sandbox path, change tracker) and SkillRegistry in `src/cleveragents/skills/context.py`. - [ ] Code [Jeff]: Wire SkillRegistry to Tool Registry for tool resolution and validation node inclusion. + - [ ] Code [Jeff]: Add context helpers for resolving bound resources and exposing plan metadata. - [ ] Docs [Jeff]: Add `docs/reference/skills_context.md` with context fields and helper methods. - [ ] Tests (Behave) [Rui]: Add `features/skill_context.feature` for sandboxed access and registry resolution. - [ ] Tests (Robot) [Rui]: Add `robot/skill_context.robot` for registry smoke tests. @@ -3420,8 +1915,9 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(skill): add skill context and registry"`. - [ ] **COMMIT (Owner: Jeff | Group: C3.inline) - Commit message: "feat(skill): add inline tool executor"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement inline tool execution with timeouts and restricted environment. + - [ ] Code [Jeff]: Implement inline tool execution with timeouts and restricted environment in `src/cleveragents/skills/inline_executor.py`. - [ ] Code [Jeff]: Ensure inline tools conform to Tool Registry schema and return structured results. + - [ ] Code [Jeff]: Add safeguards for file/network access inside inline tools (local-only for MVP). - [ ] Docs [Jeff]: Add `docs/reference/skills_inline.md` with safety constraints. - [ ] Tests (Behave) [Rui]: Add `features/skill_inline.feature` for execution and timeout handling. - [ ] Tests (Robot) [Rui]: Add `robot/skill_inline.robot` for inline tool smoke tests. @@ -3433,7 +1929,8 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group C4: Built-in Skills [Jeff + Luis]** (depends on C3) - [ ] **COMMIT (Owner: Jeff | Group: C4.file) - Commit message: "feat(skill): add file operation skills"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Implement ReadFile, WriteFile, EditFile, and DeleteFile tools with read_only enforcement. - - [ ] Code [Jeff]: Register tools in Tool Registry with resource bindings for fs/git resources. + - [ ] Code [Jeff]: Register tools in Tool Registry with resource bindings for fs/git resources and sandbox path rewrite. + - [ ] Code [Jeff]: Add content size limits and encoding normalization (UTF-8) for file tools. - [ ] Docs [Jeff]: Add `docs/reference/skills_file.md` with examples and error cases. - [ ] Tests (Behave) [Rui]: Add `features/skill_file_ops.feature` for read/write/edit/delete flows. - [ ] Tests (Robot) [Rui]: Add `robot/skill_file_ops.robot` for file ops integration. @@ -3444,6 +1941,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] **COMMIT (Owner: Jeff | Group: C4.search) - Commit message: "feat(skill): add directory and search skills"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Implement ListDir, Glob, and Grep tools with ignore patterns and size limits. - [ ] Code [Jeff]: Register tools in Tool Registry with resource bindings and sandbox awareness. + - [ ] Code [Jeff]: Enforce include/exclude glob filters from project context policies. - [ ] Docs [Jeff]: Add `docs/reference/skills_search.md` with examples. - [ ] Tests (Behave) [Rui]: Add `features/skill_search.feature` for listing/globbing/searching. - [ ] Tests (Robot) [Rui]: Add `robot/skill_search.robot` for search integration. @@ -3454,6 +1952,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] **COMMIT (Owner: Luis | Group: C4.git) - Commit message: "feat(skill): add git operation skills"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Implement read-only git tools (status, diff, log, show) for sandboxed repos. - [ ] Code [Luis]: Register git tools in Tool Registry with read-only capability metadata. + - [ ] Code [Luis]: Add path guards to ensure git tools only run inside sandbox root. - [ ] Docs [Luis]: Add `docs/reference/skills_git.md` clarifying no destructive ops in MVP. - [ ] Tests (Behave) [Rui]: Add `features/skill_git.feature` for git tool outputs. - [ ] Tests (Robot) [Rui]: Add `robot/skill_git.robot` for git tool integration. @@ -3465,7 +1964,8 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group C5: Tool Routing & Change Tracking [Luis + Jeff]** (depends on C3/C4) - [ ] **COMMIT (Owner: Luis | Group: C5.model) - Commit message: "feat(change): add ChangeSet models and invocation tracker"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Add Change/ChangeSet/ToolInvocation models and SkillInvocationTracker. - - [ ] Code [Luis]: Ensure ChangeSet stores resource references, sandbox paths, and tool metadata. + - [ ] Code [Luis]: Ensure ChangeSet stores resource references, sandbox paths, tool metadata, and timestamps. + - [ ] Code [Luis]: Add ChangeSet serialization helper for plan diff output (group by resource). - [ ] Docs [Luis]: Add `docs/reference/change_tracking.md` describing tool-to-change mapping. - [ ] Tests (Behave) [Rui]: Add `features/change_tracking.feature` for ChangeSet aggregation. - [ ] Tests (Robot) [Rui]: Add `robot/change_tracking.robot` for tracker smoke tests. @@ -3476,6 +1976,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] **COMMIT (Owner: Jeff | Group: C5.router) - Commit message: "feat(change): add tool router for providers"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Implement ToolCallRouter for OpenAI/Anthropic/LangChain tool schemas with deterministic IDs. - [ ] Code [Jeff]: Add mapping for tool/validation names and argument schemas based on Tool Registry metadata. + - [ ] Code [Jeff]: Add tool-call result normalization to match ToolInvocation schema. - [ ] Docs [Jeff]: Add `docs/reference/tool_router.md` with provider-specific mappings. - [ ] Tests (Behave) [Rui]: Add `features/tool_router.feature` for schema mapping. - [ ] Tests (Robot) [Rui]: Add `robot/tool_router.robot` for routing smoke tests. @@ -3486,6 +1987,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] **COMMIT (Owner: Luis | Group: C5.diff) - Commit message: "feat(change): add diff review artifacts"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Implement DiffBuilder and ReviewArtifact models for CLI review. - [ ] Code [Luis]: Add support for multi-resource diffs and per-resource grouping. + - [ ] Code [Luis]: Add diff output serializers for rich/plain/json formats. - [ ] Docs [Luis]: Add `docs/reference/diff_review.md` with output format. - [ ] Tests (Behave) [Rui]: Add `features/diff_review.feature` for diff generation. - [ ] Tests (Robot) [Rui]: Add `robot/diff_review.robot` for review artifacts. @@ -3494,11 +1996,12 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(change): add diff review artifacts"`. -**Parallel Group C6: Validation Pipeline [Luis + Jeff]** (depends on C5 and project validation config) +**Parallel Group C6: Validation Pipeline [Luis + Jeff]** (depends on C5 and validation attachment config) - [ ] **COMMIT (Owner: Luis | Group: C6.pipeline) - Commit message: "feat(validation): add validation pipeline and results model"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Implement ValidationCommand, ValidationResult, and ValidationPipeline using Validation attachments from Tool Registry. - [ ] Code [Luis]: Run validations at end of Execute phase only; do not re-run during Apply per spec. - [ ] Code [Luis]: Enforce required vs informational validation modes and fix-then-revalidate loop hooks. + - [ ] Code [Luis]: Persist validation summary into Plan metadata for later review. - [ ] Docs [Luis]: Add `docs/reference/validation_pipeline.md` with ordering, timeouts, and failure handling. - [ ] Tests (Behave) [Rui]: Add `features/validation_pipeline.feature` for pass/fail paths and required/informational modes. - [ ] Tests (Robot) [Rui]: Add `robot/validation_pipeline.robot` for pipeline smoke tests. @@ -3509,6 +2012,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] **COMMIT (Owner: Jeff | Group: C6.gating) - Commit message: "feat(validation): integrate validation with apply gating"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Block apply on required validation failure; surface validation artifacts for review. - [ ] Code [Jeff]: Ensure informational validation failures do not block apply but are logged in plan status. + - [ ] Code [Jeff]: Add CLI status output for validation summary (required vs informational counts). - [ ] Docs [Jeff]: Update `docs/reference/plan_actor_integration.md` with validation gating behavior. - [ ] Tests (Behave) [Rui]: Add `features/validation_gating.feature` for apply blocking. - [ ] Tests (Robot) [Rui]: Add `robot/validation_gating.robot` for end-to-end gating. @@ -3521,6 +2025,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] **COMMIT (Owner: Aditya | Group: C7.mcp) - Commit message: "feat(skill): add MCP adapter for external tools"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Aditya]: Implement MCP client adapter conforming to Tool interface with connection config. - [ ] Code [Aditya]: Register MCP tools in Tool Registry with dynamic discovery from MCP server. + - [ ] Code [Aditya]: Add timeout and retry defaults for MCP calls (local-only for MVP). - [ ] Docs [Aditya]: Add `docs/reference/skills_mcp.md` with server connection examples. - [ ] Tests (Behave) [Rui]: Add `features/skill_mcp.feature` for MCP tool calls. - [ ] Tests (Robot) [Rui]: Add `robot/skill_mcp.robot` for MCP adapter smoke test. @@ -3546,6 +2051,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Jeff]: Connect PlanLifecycleService to actor execution for Strategize and Execute phases. - [ ] Code [Jeff]: Ensure Strategize is read-only and records decisions without modifying resources. - [ ] Code [Jeff]: Ensure Execute uses sandbox resources and tool calls routed through Tool Router + ChangeSet. + - [ ] Code [Jeff]: Add plan status updates for phase start/complete/fail during actor execution. - [ ] Docs [Jeff]: Add `docs/reference/plan_actor_integration.md` with phase flow. - [ ] Tests (Behave) [Rui]: Add `features/plan_actor_integration.feature` for strategy/execute flows. - [ ] Tests (Robot) [Rui]: Add `robot/plan_actor_integration.robot` for end-to-end actor execution. @@ -3556,6 +2062,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] **COMMIT (Owner: Jeff | Group: C9.apply) - Commit message: "feat(plan): integrate change review and apply flow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Wire ChangeSet review artifacts into `plan diff` and review-before-apply flow. - [ ] Code [Jeff]: Ensure Apply merges sandbox into real resources only after required validations pass. + - [ ] Code [Jeff]: Persist apply summary (files changed, validations) back into Plan metadata for `plan status`. - [ ] Docs [Jeff]: Update CLI docs for `plan diff` and `plan apply` review output. - [ ] Tests (Behave) [Rui]: Add `features/plan_review_apply.feature` for review gate behavior. - [ ] Tests (Robot) [Rui]: Add `robot/plan_review_apply.robot` for review-before-apply path. @@ -3565,15 +2072,14 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Commit [Jeff]: `git commit -m "feat(plan): integrate change review and apply flow"`. **M3 SUCCESS CRITERIA**: -- [ ] Can define actors in YAML with skills -- [ ] Actors compile to LangGraph graphs -- [ ] Skills can execute inline Python code -- [ ] Built-in resource skills work (read/write/edit/delete files) -- [ ] MCP skill adapter connects to external servers -- [ ] Built-in provider actors work -- [ ] Tool-based change tracking builds ChangeSet from skill invocations -- [ ] Validation pipeline catches errors -- [ ] Full plan lifecycle works: Action -> Strategize (with actor) -> Execute (with actor) -> Apply +- Actor YAML schema validated; examples load and compile to LangGraph. +- Skills execute via SkillContext; built-in file/dir/search/git skills available. +- Tool-based change tracking (no output parsing) produces ChangeSet and diff review artifacts. +- MCP adapter executes a tool against a test MCP server. +- Built-in provider actors available (`openai/`, `anthropic/`, `openrouter/` as configured). +- Validation pipeline runs validation attachments and blocks apply on required failure. +- Plan lifecycle uses actors for Strategize/Execute and applies ChangeSet after review. +- `nox` passes with coverage >=97% across actor/skill/change-tracking suites. **--- MERGE POINT 1: After M3, all workstreams coordinate ---** @@ -3713,10 +2219,15 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group E1: Subplan Domain [Luis + Rui]** - [ ] **COMMIT (Owner: Luis | Group: E1.domain) - Commit message: "feat(domain): add subplan config and status models"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Luis]: Add `ExecutionMode`, `MergeStrategy`, `SubplanConfig`, `SubplanStatus`, and `SubplanAttempt` models. - - [ ] Code [Luis]: Extend `Plan` with parent/root IDs, subplan statuses, and helpers (`is_subplan`, `has_subplans`). + - [ ] Code [Luis]: Add `ExecutionMode` enum (sequential, parallel, hybrid) with validation guards. + - [ ] Code [Luis]: Add `MergeStrategy` enum (three_way, sequential, json) with defaults. + - [ ] Code [Luis]: Add `SubplanConfig` model fields: parent_plan_id, spawn_decision_id, dependencies, max_parallel, merge_strategy, automation_profile_override, invariants_override, context_view_override. + - [ ] Code [Luis]: Add `SubplanStatus` model fields: subplan_id, state, started_at, completed_at, error_message, changeset_id. + - [ ] Code [Luis]: Add `SubplanAttempt` model fields: attempt_id (ULID), subplan_id, attempt_number, started_at, completed_at, error_details. + - [ ] Code [Luis]: Extend `Plan` with `subplan_config`, `subplan_statuses`, `spawn_decision_id`, and helpers (`is_subplan`, `has_subplans`, `child_count`). + - [ ] Code [Luis]: Add DecisionType constants for `subplan_spawn` and `subplan_parallel_spawn` and ensure models reference them. - [ ] Docs [Luis]: Add `docs/reference/subplan_model.md`. - - [ ] Tests (Behave) [Rui]: Add `features/subplan_model.feature` scenarios. + - [ ] Tests (Behave) [Rui]: Add `features/subplan_model.feature` scenarios for config validation, dependency cycles, and parent/root helpers. - [ ] Tests (Robot) [Rui]: Add `robot/subplan_model.robot` smoke tests. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_model_bench.py` for model validation. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). @@ -3725,10 +2236,13 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group E2: Subplan Spawning [Jeff + Aditya]** (depends on D2 + E1) - [ ] **COMMIT (Owner: Jeff | Group: E2.service) - Commit message: "feat(service): add subplan service and spawn workflow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Implement `SubplanService` with `spawn_subplan`, `spawn_batch`, tree queries, bounded context builder. + - [ ] Code [Jeff]: Implement `SubplanService` with `spawn_subplan`, `spawn_batch`, tree queries, and bounded context builder. + - [ ] Code [Jeff]: Build bounded context from parent plan decisions + project context policies; enforce token/file limits. + - [ ] Code [Jeff]: Inherit automation profile + invariants from parent plan; allow subplan overrides from decisions. + - [ ] Code [Jeff]: Persist subplan config into child Plan metadata (`subplan_config`) and link `spawn_decision_id`. - [ ] Code [Jeff]: Link SUBPLAN_SPAWN decisions to created subplans and status tracking. - [ ] Docs [Jeff]: Add `docs/reference/subplan_service.md`. - - [ ] Tests (Behave) [Rui]: Add subplan spawn scenarios. + - [ ] Tests (Behave) [Rui]: Add subplan spawn scenarios (inheritance, overrides, dependency ordering). - [ ] Tests (Robot) [Rui]: Add subplan spawn integration tests. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_spawn_bench.py` for spawn throughput. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). @@ -3736,8 +2250,10 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Commit [Jeff]: `git commit -m "feat(service): add subplan service and spawn workflow"`. - [ ] **COMMIT (Owner: Aditya | Group: E2.actor) - Commit message: "feat(actor): add plan_subplan tool and decision emission"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Aditya]: Add `plan_subplan` tool to strategy actors and emit SUBPLAN_SPAWN decisions. + - [ ] Code [Aditya]: Support `parallel=true` to emit SUBPLAN_PARALLEL_SPAWN and include dependency list. + - [ ] Code [Aditya]: Include merge strategy, resource scope, and context view overrides in decision payload. - [ ] Docs [Aditya]: Update actor YAML examples for subplan emission. - - [ ] Tests (Behave) [Rui]: Add scenarios for subplan decision emission. + - [ ] Tests (Behave) [Rui]: Add scenarios for subplan decision emission (parallel + dependencies). - [ ] Tests (Robot) [Rui]: Add actor tool integration smoke tests. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_actor_tool_bench.py` for tool invocation overhead. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). @@ -3747,7 +2263,9 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group E3: Parallel Execution [Luis + Jeff]** (depends on E1/E2) - [ ] **COMMIT (Owner: Luis | Group: E3.exec) - Commit message: "feat(service): add subplan scheduler and execution"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Add subplan scheduler with `max_parallel`, dependency ordering, and fail-fast handling. - - [ ] Code [Luis]: Track status updates for subplans and propagate to parent plan. + - [ ] Code [Luis]: Support sequential and parallel execution modes based on SubplanConfig. + - [ ] Code [Luis]: Track status updates for subplans and propagate to parent plan (processing/complete/errored). + - [ ] Code [Luis]: Add cancellation propagation from parent to child subplans. - [ ] Docs [Luis]: Add `docs/reference/subplan_execution.md`. - [ ] Tests (Behave) [Rui]: Add parallel + dependency execution scenarios. - [ ] Tests (Robot) [Rui]: Add parallel execution integration tests. @@ -3760,6 +2278,8 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] **COMMIT (Owner: Jeff | Group: E4.merge) - Commit message: "feat(merge): add subplan merge strategies"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Add three-way merge strategy for file changes and conflict markers. - [ ] Code [Luis]: Add sequential merge and JSON merge strategies; expose merge result artifacts. + - [ ] Code [Jeff]: Add conflict artifact model (file_path, conflict_type, base/left/right snippets). + - [ ] Code [Luis]: Store merge output as ChangeSet and attach to parent plan for review. - [ ] Docs [Jeff]: Add `docs/reference/subplan_merge.md`. - [ ] Tests (Behave) [Rui]: Add merge + conflict scenarios. - [ ] Tests (Robot) [Rui]: Add merge integration tests for multi-subplan plans. @@ -3772,6 +2292,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] **COMMIT (Owner: Hamza | Group: E5.multi) - Commit message: "feat(plan): add multi-project subplan support"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Allow plans to target multiple projects with separate resource link contexts. - [ ] Code [Luis]: Ensure sandbox isolation and cross-project dependency resolution. + - [ ] Code [Hamza]: Add plan metadata to track project-specific ChangeSets and validation summaries. - [ ] Docs [Hamza]: Add `docs/reference/multi_project_plans.md`. - [ ] Tests (Behave) [Rui]: Add multi-project subplan scenarios. - [ ] Tests (Robot) [Rui]: Add multi-project integration tests. @@ -3974,7 +2495,10 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group G1: Large-Project Decomposition [Jeff + Luis]** - [ ] **COMMIT (Owner: Jeff | Group: G1.decompose) - Commit message: "feat(plan): add large-project decomposition and dependency closure"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Add hierarchical decomposition with 4+ levels and bounded context per subplan. + - [ ] Code [Jeff]: Implement decomposition heuristics (max_files_per_subplan, max_tokens_per_subplan, language/dir clustering). - [ ] Code [Luis]: Add dependency closure computation for large graphs and DAG execution ordering. + - [ ] Code [Luis]: Add bounded dependency closure with cutoff thresholds and memoization for 10K+ files. + - [ ] Code [Jeff]: Record decomposition decisions in DecisionService (strategy_choice + subplan_spawn entries). - [ ] Docs [Jeff]: Add `docs/reference/large_project_decomposition.md`. - [ ] Tests (Behave) [Rui]: Add deep hierarchy + dependency closure scenarios. - [ ] Tests (Robot) [Rui]: Add large-project decomposition integration tests. @@ -3986,7 +2510,9 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group G2: Checkpointing & Rollback [Luis]** - [ ] **COMMIT (Owner: Luis | Group: G2.checkpoint) - Commit message: "feat(checkpoint): add checkpointing and rollback"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Add checkpoint declarations for tools and plan-level rollback policy. + - [ ] Code [Luis]: Add `checkpoints` table (checkpoint_id ULID, plan_id, sandbox_ref, created_at, metadata_json). - [ ] Code [Luis]: Implement `plan rollback ` command. + - [ ] Code [Luis]: Implement git-worktree checkpoint snapshots (commit hash or patch) and rollback restore. - [ ] Docs [Luis]: Add `docs/reference/checkpointing.md`. - [ ] Tests (Behave) [Rui]: Add checkpoint/rollback scenarios. - [ ] Tests (Robot) [Rui]: Add rollback integration tests. @@ -3998,6 +2524,8 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group G3: Semantic Validation [Luis]** - [ ] **COMMIT (Owner: Luis | Group: G3.semantic) - Commit message: "feat(validation): add semantic validation service"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Add semantic validation hooks during strategize/execute and error-pattern checks. + - [ ] Code [Luis]: Add rule registry for semantic validators (dependency cycles, API misuse, missing symbols). + - [ ] Code [Luis]: Integrate semantic validation results into ValidationPipeline as informational by default. - [ ] Docs [Luis]: Add `docs/reference/semantic_validation.md`. - [ ] Tests (Behave) [Rui]: Add semantic validation scenarios. - [ ] Tests (Robot) [Rui]: Add semantic validation integration tests. @@ -4009,7 +2537,9 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group G4: Context Tiers & Views [Hamza + Rui]** - [ ] **COMMIT (Owner: Hamza | Group: G4.context) - Commit message: "feat(context): add hot/warm/cold tiers and actor views"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Implement hot/warm/cold tiers with indexing, LRU eviction, and promotion/demotion. + - [ ] Code [Hamza]: Add tier storage backends (in-memory hot, sqlite warm, file-backed cold). - [ ] Code [Hamza]: Add per-actor context views (strategist/executor/reviewer) and filtered presentation. + - [ ] Code [Hamza]: Add summarization hook when demoting to cold tier. - [ ] Docs [Hamza]: Add `docs/reference/context_tiers.md`. - [ ] Tests (Behave) [Rui]: Add context tier scenarios. - [ ] Tests (Robot) [Rui]: Add context tier integration tests. @@ -4021,6 +2551,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group G5: Cost & Risk Estimation [Hamza]** - [ ] **COMMIT (Owner: Hamza | Group: G5.estimate) - Commit message: "feat(estimation): add cost and risk estimation actor"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Add optional `estimation_actor` role and cost/risk estimation outputs. + - [ ] Code [Hamza]: Persist estimation output to plan metadata (cost_estimate, risk_score, duration_estimate). - [ ] Docs [Hamza]: Add `docs/reference/estimation.md` with output format. - [ ] Tests (Behave) [Rui]: Add estimation scenarios. - [ ] Tests (Robot) [Rui]: Add estimation integration smoke tests. @@ -4032,6 +2563,7 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Parallel Group G6: CLI Polish [All]** - [ ] **COMMIT (Owner: Jeff | Group: G6.cli) - Commit message: "chore(cli): polish help and output"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [All]: Standardize help text, progress indicators, and error messages with recovery hints. + - [ ] Code [All]: Ensure `--format` outputs are consistent (rich/color/table/plain/json/yaml) across core commands. - [ ] Docs [All]: Update CLI output examples where needed. - [ ] Tests (Robot) [Rui]: Add CLI UX smoke tests for critical commands. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/cli_render_bench.py` for output rendering overhead. @@ -4068,44 +2600,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(interfaces): add server client stubs"`. -- [ ] **Stage G2: Checkpointing & Rollback** (Day 32-33) **[Luis]** - - [ ] **G2.1** [Luis] Skill-level checkpoint declarations - - [ ] **G2.2** [Luis] Plan-level rollback policy - - [ ] **G2.3** [Luis] Rollback to checkpoint command - - [ ] **G2.4** [Rui] Checkpoint/rollback tests - -- [ ] **Stage G3: Semantic Validation Framework** (Day 33-34) **[Luis]** - - [ ] **G3.1** [Luis] Decision-time validation in Strategize: - - [ ] Every decision includes semantic validation - - [ ] Record `validation_performed` list on each decision - - [ ] Validate chosen option against alternatives - - [ ] Check for breaking changes, compatibility issues - - [ ] **G3.2** [Luis] Execution-time semantic guards: - - [ ] Actor configs can include validation nodes - - [ ] `validate_api_compatibility` - check for breaking API changes - - [ ] `validate_type_safety` - check types are preserved - - [ ] `auto_migrate` - generate migration plan for breaking changes - - [ ] Guards can auto-fix simple issues or escalate complex ones - - [ ] **G3.3** [Luis] Invariant enforcement system: - - [ ] User-defined invariants per project (see Section 15) - - [ ] Check invariants at each major step - - [ ] Fail execution on violation (if severity=error) - - [ ] Record invariant check results in plan metadata - - [ ] **G3.4** [Luis] Error pattern database: - - [ ] Store historical failures with context - - [ ] Identify patterns: "Async conversion in X module often causes Y" - - [ ] Before execution, check for known patterns - - [ ] Add preventive checks based on patterns - - [ ] Learn from successful corrections - - [ ] **G3.5** [Luis] Implement `SemanticValidationService`: - - [ ] Method `validate_decision(decision) -> ValidationResult` - - [ ] Method `check_invariants(project, changes) -> list[InvariantResult]` - - [ ] Method `check_error_patterns(context) -> list[PatternMatch]` - - [ ] Method `suggest_preventive_checks(pattern) -> list[Check]` - - [ ] **G3.6** [Rui] Semantic validation tests: - - [ ] Scenario: Decision with breaking API change is flagged - - [ ] Scenario: Invariant violation detected during execution - - [ ] Scenario: Known error pattern triggers preventive check +**M7 SUCCESS CRITERIA** (Post-Day 30): +- `agents [--data-dir PATH] [--config-path PATH] connect ` establishes connection to an external server. +- Plans can be synced and executed on a remote server. +- Real-time updates received via WebSocket from server. +- Remote projects can be specified and executed on server. - [ ] **Stage G4: Context Tiers** (Day 34-35) **[Hamza]** - [ ] **G4.1** [Hamza] Hot context management (10-20 files): @@ -4192,9 +2691,12 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is **Parallel Group 10A: Async Infrastructure [Luis]** - [ ] **COMMIT (Owner: Luis | Group: 10A.async) - Commit message: "feat(async): add async command execution and workers"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Implement async command execution per ADR-002 with cancellation and timeout handling. - - [ ] Code [Luis]: Add background worker orchestration for plan lifecycle events. - - [ ] Docs [Luis]: Update `docs/reference/async_architecture.md` with execution flow and shutdown rules. - - [ ] Tests (Behave) [Rui]: Add `features/async_execution.feature` for async command handling. + - [ ] Code [Luis]: Add `AsyncJob` model and `async_jobs` table (plan_id, phase, status, payload_json, created_at, started_at, finished_at). + - [ ] Code [Luis]: Add AsyncWorker orchestrator with polling loop, max_workers config, and graceful shutdown hooks. + - [ ] Code [Luis]: Add job enqueue hooks for plan execute/apply when async is enabled via config flag (no new CLI flags). + - [ ] Code [Luis]: Add cancellation token support and ensure cancellation propagates to tool execution. + - [ ] Docs [Luis]: Update `docs/reference/async_architecture.md` with execution flow, job states, and shutdown rules. + - [ ] Tests (Behave) [Rui]: Add `features/async_execution.feature` for async command handling (enqueue, worker pick-up, cancel). - [ ] Tests (Robot) [Rui]: Add `robot/async_execution.robot` smoke tests. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/async_execution_bench.py` for worker scheduling overhead. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). @@ -4202,6 +2704,8 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Commit [Luis]: `git commit -m "feat(async): add async command execution and workers"`. - [ ] **COMMIT (Owner: Luis | Group: 10A.retry) - Commit message: "feat(async): wire retry policies into services"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Integrate retry/circuit breaker policies into service layer operations. + - [ ] Code [Luis]: Add retry policy configuration keys (max_attempts, base_delay, max_delay, jitter) to settings. + - [ ] Code [Luis]: Ensure retries are only applied to idempotent operations (repository reads, validation calls) and never to applies. - [ ] Docs [Luis]: Document retry policy defaults and override points. - [ ] Tests (Behave) [Rui]: Add retry/circuit breaker behavior scenarios. - [ ] Tests (Robot) [Rui]: Add resilience smoke tests. @@ -4214,6 +2718,7 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] **COMMIT (Owner: Brent | Group: 10B.review) - Commit message: "docs(qa): add review playbook and priority matrix"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Docs [Brent]: Create `docs/development/review_playbook.md` with focus areas and skip rules. - [ ] Docs [Brent]: Add priority matrix and review SLA guidance. + - [ ] Docs [Brent]: Add checklist templates for architecture review, CLI review, and DB migration review. - [ ] Tests (Behave) [Rui]: Add scenarios validating review playbook references exist. - [ ] Tests (Robot) [Rui]: Add docs build smoke test covering the new guide. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/docs_build_bench.py` for docs build baseline. @@ -4224,8 +2729,9 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is **Parallel Group 10C: Validation Testing Support [Brent + Luis]** - [ ] **COMMIT (Owner: Brent | Group: 10C.edge) - Commit message: "test(validation): add edge case suites"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Brent]: Add shared edge-case fixtures under `features/fixtures/validation/`. + - [ ] Code [Brent]: Add fixtures for malformed tool outputs, missing resources, and validation timeouts. - [ ] Docs [Brent]: Update `docs/development/testing.md` with validation test catalog. - - [ ] Tests (Behave) [Rui]: Add edge-case scenarios for concurrency, conflicts, and rollbacks. + - [ ] Tests (Behave) [Rui]: Add edge-case scenarios for concurrency, conflicts, rollbacks, and timeouts. - [ ] Tests (Robot) [Rui]: Add integration coverage for edge-case suites. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/validation_edge_bench.py` for edge-case runtime. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). @@ -4233,6 +2739,7 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Commit [Brent]: `git commit -m "test(validation): add edge case suites"`. - [ ] **COMMIT (Owner: Luis | Group: 10C.semantic) - Commit message: "test(validation): add semantic validation suites"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Add semantic validation fixtures and error-pattern samples. + - [ ] Code [Luis]: Add fixtures for language-porting mismatches and dependency graph violations. - [ ] Docs [Luis]: Document semantic validation coverage expectations. - [ ] Tests (Behave) [Rui]: Add semantic validation scenarios. - [ ] Tests (Robot) [Rui]: Add semantic validation integration tests. @@ -4242,6 +2749,7 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Commit [Luis]: `git commit -m "test(validation): add semantic validation suites"`. - [ ] **COMMIT (Owner: Brent | Group: 10C.performance) - Commit message: "test(perf): add scale test fixtures"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Brent]: Add scale fixtures for 1K/5K/10K file repos in `features/fixtures/scale/`. + - [ ] Code [Brent]: Add scriptless fixture generator instructions (documented, no helper scripts). - [ ] Docs [Brent]: Add scale test runbook and environment notes. - [ ] Tests (Behave) [Rui]: Add scale test scenarios validating thresholds. - [ ] Tests (Robot) [Rui]: Add large-project Robot tests for performance runs. @@ -4258,1023 +2766,254 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is **Note**: With automated quality gates in place (Section 0), Brent only reviews security changes after automated scanning. -- [ ] **Stage SEC1: Remove eval() Vulnerability** (Day 1-2) **[Luis - CRITICAL]** - - [ ] Test with code containing unused imports - - [ ] Commit: "feat(qa): add ruff linting to pre-commit" - - [ ] **0.1e** [Brent] Add pyright type checking hook: - ```yaml - - repo: local - hooks: - - id: pyright - name: Type check with pyright - entry: pyright - language: system - types: [python] - require_serial: true - ``` - - [ ] Ensure pyright is installed via dev dependencies - - [ ] Test with code containing type errors - - [ ] Commit: "feat(qa): add pyright to pre-commit" - - [ ] **0.1f** [Brent] Add security scanning with bandit: - - [ ] Add `bandit[toml]>=1.7.5` to dev dependencies - - [ ] Create `pyproject.toml` section for bandit config: - ```toml - [tool.bandit] - exclude_dirs = ["tests", "features", "benchmarks"] - skips = ["B101"] # Skip assert_used test in test files - ``` - - [ ] Add bandit hook: - ```yaml - - repo: https://github.com/PyCQA/bandit - rev: '1.7.5' - hooks: - - id: bandit - args: ['-c', 'pyproject.toml'] - additional_dependencies: ["bandit[toml]"] - ``` - - [ ] Commit: "feat(qa): add bandit security scanning" - - [ ] **0.1g** [Brent] Add vulture for dead code detection: - - [ ] Add `vulture>=2.10` to dev dependencies - - [ ] Create `vulture_whitelist.py` for false positives - - [ ] Add vulture hook: - ```yaml - - repo: local - hooks: - - id: vulture - name: Find dead code with vulture - entry: vulture - language: system - types: [python] - args: [--min-confidence, "80", "--exclude", "*/tests/*,*/features/*"] - ``` - - [ ] Commit: "feat(qa): add vulture dead code detection" - - [ ] **0.1h** [Brent] Add test runner hook for changed files: - ```yaml - - repo: local - hooks: - - id: pytest-changed - name: Run tests for changed files - entry: bash -c 'git diff --cached --name-only | grep -E "\.py$" | xargs -I {} pytest tests/{} 2>/dev/null || true' - language: system - pass_filenames: false - always_run: true - ``` - - [ ] Commit: "feat(qa): add test runner for changed files" - - [ ] **0.1i** [Brent] Add semgrep for pattern-based checks: - - [ ] Install semgrep: Add `semgrep>=1.45.0` to dev dependencies - - [ ] Create `.semgrep.yml` with initial rules: - ```yaml - rules: - - id: no-eval - pattern: eval(...) - message: "eval() is dangerous and banned" - languages: [python] - severity: ERROR - - id: no-exec - pattern: exec(...) - message: "exec() is dangerous and banned" - languages: [python] - severity: ERROR - - id: no-bare-except - pattern: | - try: - ... - except: - ... - message: "Use specific exception types" - languages: [python] - severity: WARNING - ``` - - [ ] Add semgrep hook: - ```yaml - - repo: local - hooks: - - id: semgrep - name: Scan with semgrep - entry: semgrep --config=.semgrep.yml - language: system - types: [python] - ``` - - [ ] Commit: "feat(qa): add semgrep pattern scanning" - - [ ] **0.1j** [Brent] Add commit message linting: - ```yaml - - repo: https://github.com/commitizen-tools/commitizen - rev: v3.13.0 - hooks: - - id: commitizen - stages: [commit-msg] - ``` - - [ ] Configure conventional commits format - - [ ] Commit: "feat(qa): add commit message linting" - - [ ] **0.1k** [Brent] Create developer setup script `scripts/setup-dev.sh`: - ```bash - #!/bin/bash - set -euo pipefail - echo "Setting up pre-commit hooks..." - pip install pre-commit - pre-commit install - pre-commit install --hook-type commit-msg - echo "Running initial quality checks..." - pre-commit run --all-files - echo "Developer environment ready!" - ``` - - [ ] Make executable: `chmod +x scripts/setup-dev.sh` - - [ ] Update README.md developer setup instructions - - [ ] Commit: "feat(qa): add developer setup script" - - **Day 2: CI/CD Pipeline with GitHub Actions (or GitLab CI equivalent)** - - - [ ] **0.2** [Brent] Create GitHub Actions workflow for PR validation: - - [ ] **0.2a** [Brent] Create `.github/workflows/pr-validation.yml`: - ```yaml - name: PR Validation - on: - pull_request: - types: [opened, synchronize, reopened] - jobs: - quality-gates: - name: Quality Gates - runs-on: ubuntu-latest - ``` - - [ ] Commit: "feat(ci): add PR validation workflow scaffold" - - [ ] **0.2b** [Brent] Add Python setup and dependency caching: - ```yaml - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # For proper git history - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.13' - - - name: Cache pip packages - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} - restore-keys: | - ${{ runner.os }}-pip- - ``` - - [ ] Commit: "feat(ci): add Python setup with caching" - - [ ] **0.2c** [Brent] Install dependencies and run formattin check: - ```yaml - - name: Install dependencies - run: | - pip install -e .[dev,tests] - pip install pre-commit - - - name: Check code formatting - run: | - pre-commit run ruff-format --all-files --show-diff-on-failure - ``` - - [ ] Commit: "feat(ci): add formatting check" - - [ ] **0.2d** [Brent] Add linting step: - ```yaml - - name: Lint with Ruff - run: | - ruff check . --output-format=github - ``` - - [ ] Use GitHub annotations format for inline PR comments - - [ ] Commit: "feat(ci): add linting with GitHub annotations" - - [ ] **0.2e** [Brent] Add type checking step: - ```yaml - - name: Type check with pyright - run: | - pyright --outputjson > pyright-results.json || true - python scripts/parse-pyright-results.py pyright-results.json - ``` - - [ ] Create `scripts/parse-pyright-results.py` to format errors as GitHub annotations - - [ ] Commit: "feat(ci): add type checking with annotations" - - [ ] **0.2f** [Brent] Add security scanning: - ```yaml - - name: Security scan with bandit - run: | - bandit -r src/ -f json -o bandit-results.json || true - python scripts/parse-bandit-results.py bandit-results.json - - - name: Check for vulnerabilities - run: | - pip install safety - safety check --json > safety-results.json || true - python scripts/parse-safety-results.py safety-results.json - ``` - - [ ] Create parsing scripts for annotations - - [ ] Commit: "feat(ci): add security scanning" - - [ ] **0.2g** [Brent] Add test execution with coverage: - ```yaml - - name: Run tests with coverage - run: | - nox -s unit_tests -- --junit-xml=test-results.xml - nox -s coverage_report - - - name: Upload test results - if: always() - uses: actions/upload-artifact@v4 - with: - name: test-results - path: test-results.xml - - - name: Comment coverage on PR - uses: py-cov-action/python-coverage-comment-action@v3 - with: - GITHUB_TOKEN: ${{ github.token }} - MINIMUM_GREEN: 85 - MINIMUM_ORANGE: 70 - ``` - - [ ] Commit: "feat(ci): add test execution with coverage reporting" - - [ ] **0.2h** [Brent] Add semgrep scanning: - ```yaml - - name: Semgrep scan - uses: returntocorp/semgrep-action@v1 - with: - config: .semgrep.yml - generateSarif: true - - - name: Upload SARIF - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: semgrep.sarif - ``` - - [ ] Commit: "feat(ci): add semgrep scanning with SARIF" - - [ ] **0.2i** [Brent] Add PR comment summary: - ```yaml - - name: Generate quality report - if: always() - run: | - python scripts/generate-quality-report.py \ - --coverage coverage.xml \ - --pyright pyright-results.json \ - --bandit bandit-results.json \ - --output pr-comment.md - - - name: Comment on PR - if: always() - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs'); - const comment = fs.readFileSync('pr-comment.md', 'utf8'); - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: comment - }); - ``` - - [ ] Create `scripts/generate-quality-report.py` to aggregate results - - [ ] Commit: "feat(ci): add PR quality report comment" - - [ ] **0.2j** [Brent] Add job failure conditions: - ```yaml - - name: Check quality gates - run: | - python scripts/check-quality-gates.py \ - --coverage-min 85 \ - --type-errors-max 0 \ - --security-issues-max 0 - ``` - - [ ] Script exits with code 1 if any gate fails - - [ ] Commit: "feat(ci): add quality gate enforcement" - - [ ] **0.2k** [Brent] Create branch protection rules documentation: - - [ ] Document in `docs/development/branch-protection.md`: - - [ ] Require PR validation workflow to pass - - [ ] Require at least 1 review (Brent reviews all) - - [ ] Dismiss stale reviews on new commits - - [ ] Require branches to be up to date before merging - - [ ] Commit: "docs(qa): add branch protection documentation" - - **Day 3: Advanced Automation and Monitoring** - - - [ ] **0.3** [Brent] Set up advanced quality automation: - - [ ] **0.3a** [Brent] Create nightly quality check workflow `.github/workflows/nightly-quality.yml`: - ```yaml - name: Nightly Quality Check - on: - schedule: - - cron: '0 0 * * *' # Run at midnight UTC - workflow_dispatch: # Allow manual trigger - ``` - - [ ] Run full test suite including slow tests - - [ ] Run mutation testing with mutmut - - [ ] Generate comprehensive reports - - [ ] Commit: "feat(ci): add nightly quality checks" - - [ ] **0.3b** [Brent] Add dependency update automation: - ```yaml - name: Dependency Updates - on: - schedule: - - cron: '0 9 * * MON' # Weekly on Mondays - jobs: - update-deps: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Update dependencies - run: | - pip install pip-tools - pip-compile --upgrade - pip install .[dev,tests] - pre-commit autoupdate - - name: Create Pull Request - uses: peter-evans/create-pull-request@v5 - with: - title: "chore: update dependencies" - body: "Automated dependency updates" - branch: deps/automated-update - ``` - - [ ] Commit: "feat(ci): add dependency update automation" - - [ ] **0.3c** [Brent] Create complexity monitoring: - - [ ] Install `radon>=6.0.1` for complexity analysis - - [ ] Add to pre-commit: - ```yaml - - repo: local - hooks: - - id: complexity-check - name: Check code complexity - entry: radon cc src/ -nb -s - language: system - pass_filenames: false - ``` - - [ ] Add to CI pipeline with threshold enforcement - - [ ] Commit: "feat(qa): add complexity monitoring" - - [ ] **0.3d** [Brent] Set up performance regression detection: - - [ ] Create `benchmarks/` directory with ASV benchmarks - - [ ] Add benchmark job to CI: - ```yaml - - name: Run benchmarks - run: | - asv machine --yes - asv run HEAD^..HEAD - asv compare HEAD^ HEAD - ``` - - [ ] Fail if performance regresses >10% - - [ ] Commit: "feat(ci): add performance regression detection" - - [ ] **0.3e** [Brent] Create quality metrics dashboard script: - - [ ] Script `scripts/generate-metrics-dashboard.py`: - - [ ] Aggregate coverage trends - - [ ] Track type checking progress - - [ ] Monitor code complexity trends - - [ ] Count TODO/FIXME/HACK comments - - [ ] Generate markdown report - - [ ] Run weekly and post to team - - [ ] Commit: "feat(qa): add quality metrics dashboard" - - [ ] **0.3f** [Brent] Set up documentation quality checks: - - [ ] Add doc linting: - ```yaml - - repo: local - hooks: - - id: doc-quality - name: Check documentation quality - entry: python scripts/check-docstrings.py - language: system - types: [python] - ``` - - [ ] Verify all public functions have docstrings - - [ ] Check docstring format (Google style) - - [ ] Commit: "feat(qa): add documentation quality checks" - - [ ] **0.3g** [Brent] Create ADR compliance checker: - - [ ] Script `scripts/check-adr-compliance.py`: - - [ ] Parse ADRs from `docs/architecture/decisions/` - - [ ] Check code against ADR requirements - - [ ] Flag violations (e.g., sync code in async modules) - - [ ] Add to pre-commit and CI - - [ ] Commit: "feat(qa): add ADR compliance checking" - - [ ] **0.3h** [Brent] Set up coverage delta checking: - - [ ] Modify CI to track coverage changes: - ```yaml - - name: Check coverage delta - run: | - git fetch origin main - nox -s coverage_report -- --compare-branch=origin/main - python scripts/check-coverage-delta.py --min-delta=-0.5 - ``` - - [ ] Fail if coverage drops more than 0.5% - - [ ] Commit: "feat(ci): add coverage delta enforcement" - - [ ] **0.3i** [Brent] Create PR template with quality checklist: - - [ ] Create `.github/pull_request_template.md`: - ```markdown - ## Description - Brief description of changes - - ## Quality Checklist - - [ ] Tests added/updated for new functionality - - [ ] Type hints added for all new functions - - [ ] Docstrings added/updated - - [ ] No new linting warnings - - [ ] Coverage maintained or increased - - [ ] ADRs followed - - [ ] Security implications considered - - ## Testing - How has this been tested? - ``` - - [ ] Commit: "feat(qa): add PR template with quality checklist" - - [ ] **0.3j** [Brent] Document quality automation setup: - - [ ] Create `docs/development/quality-automation.md`: - - [ ] Pre-commit hook reference - - [ ] CI/CD pipeline overview - - [ ] How to run quality checks locally - - [ ] How to handle quality gate failures - - [ ] Exemption process (when needed) - - [ ] Add to developer onboarding - - [ ] Commit: "docs(qa): document quality automation" - - **Post Day 3: Transition Plan** - - - [ ] **0.4** [Brent] Transition to selective manual review (Days 4-8): - - [ ] **0.4a** [Brent] Focus manual reviews on: - - [ ] Architectural decisions - - [ ] Complex algorithms - - [ ] API contracts - - [ ] Security-sensitive code - - [ ] Skip reviewing: formatting, basic types, simple CRUD - - [ ] **0.4b** [Brent] Create review priority matrix: - - [ ] P0: Security, API changes, architecture - - [ ] P1: Complex business logic, algorithms - - [ ] P2: Normal features - - [ ] P3: Tests, docs, refactoring (trust automation) - - - [ ] **0.5** [Brent] Transition to high-impact work (After Day 8): - - [ ] **0.5a** [Luis + Brent] Move to validation pipeline work: - - [ ] Help implement semantic validation in execution actors - - [ ] Create validation test suites - - [ ] Document validation patterns - - [ ] **0.5b** [Luis + Brent] Assist with change tracking edge cases: - - [ ] Test tool-based change tracking thoroughly - - [ ] Find and fix edge cases - - [ ] Create comprehensive test scenarios +**Parallel Group SEC1: Remove eval() usage [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: SEC1.eval) - Commit message: "fix(security): remove eval-based config parsing"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Audit and remove all `eval`/`exec`/`compile` usage from production config paths. + - [ ] Docs [Luis]: Add `docs/reference/security_eval.md` with replacement patterns. + - [ ] Tests (Behave) [Rui]: Add `features/security_eval.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add `robot/security_eval.robot` smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_eval_bench.py` for config parsing baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "fix(security): remove eval-based config parsing"`. +**Parallel Group SEC2: Template Injection Prevention [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: SEC2.template) - Commit message: "fix(security): harden template rendering"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Replace unsafe template usage with a sandboxed renderer and strict token set. + - [ ] Docs [Luis]: Add `docs/reference/template_security.md` with safe patterns. + - [ ] Tests (Behave) [Rui]: Add `features/security_templates.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add `robot/security_templates.robot` smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_template_bench.py` for render baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "fix(security): harden template rendering"`. +**Parallel Group SEC3: Exception Handling Audit [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: SEC3.exceptions) - Commit message: "fix(security): enforce explicit exception handling"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Replace silent exception handling with explicit errors and context propagation. + - [ ] Docs [Luis]: Document error propagation standards and logging rules. + - [ ] Tests (Behave) [Rui]: Add `features/security_exceptions.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add exception handling integration smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_exception_bench.py` for error path overhead baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "fix(security): enforce explicit exception handling"`. +**Parallel Group SEC4: Async Lifecycle Correctness [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: SEC4.async) - Commit message: "fix(security): close async resources and leaks"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Close async resources, checkpoint files, and subscription leaks with retention policies. + - [ ] Docs [Luis]: Add `docs/reference/async_safety.md` on cleanup rules. + - [ ] Tests (Behave) [Rui]: Add `features/security_async.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add async cleanup integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_async_cleanup_bench.py` for cleanup overhead baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "fix(security): close async resources and leaks"`. +**Parallel Group SEC5: Secrets Management [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: SEC5.secrets) - Commit message: "feat(security): add secrets masking and validation"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Mask credentials in logs, validate required keys, and block secret leakage in outputs. + - [ ] Docs [Hamza]: Add `docs/reference/secrets_handling.md`. + - [ ] Tests (Behave) [Rui]: Add `features/security_secrets.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add secrets handling integration smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_secrets_bench.py` for masking overhead baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(security): add secrets masking and validation"`. ---- +**Parallel Group SEC6: Read-Only Enforcement [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: SEC6.readonly) - Commit message: "feat(security): enforce read-only actions"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Validate read-only actions only use read-only skills at execution time. + - [ ] Docs [Luis]: Add `docs/reference/read_only_actions.md`. + - [ ] Tests (Behave) [Rui]: Add `features/security_readonly.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add read-only enforcement integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_readonly_bench.py` for enforcement overhead baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "feat(security): enforce read-only actions"`. + - [ ] Note: Safety profile enforcement is deferred; see Section 18 POST1. -### Section 11: Security & Safety [WORKSTREAM F - Luis + Brent] - -**Target: Throughout project, critical items by Day 14** - -**CRITICAL SECURITY BLOCKERS** (Must be addressed before any production use) - -- [ ] **Stage SEC1: Remove eval() Vulnerability** (Day 1-2) **[Luis - CRITICAL]** - - [ ] Code: Remove all eval() from config parsing - - [ ] **SEC1.1** [Luis] Audit all files for `eval()` usage in `src/cleveragents/`: - - [ ] Search for `eval(`, `exec(`, `compile(` calls - - [ ] Document each occurrence with file path and line number - - [ ] Classify as: (a) test-only, (b) removable, (c) requires redesign - - [ ] **SEC1.2** [Luis] Replace eval-based config transforms: - - [ ] Create whitelist of allowed transform operators - - [ ] Implement safe expression parser (no arbitrary code execution) - - [ ] Or: transforms must reference named functions from a registry - - [ ] **SEC1.3** [Luis] Remove eval from reactive routing config: - - [ ] Audit `src/cleveragents/reactive/config_parser.py` - - [ ] Replace dynamic code execution with safe alternatives - - [ ] **SEC1.4** [Brent] Code review all eval removal changes - - [ ] Tests: Security tests - - [ ] **SEC1.5** [Rui] Write Behave scenarios in `features/security_eval.feature`: - - [ ] Scenario: Config with code injection attempt is rejected - - [ ] Scenario: Malicious transform expression does not execute - - [ ] Scenario: Valid config still works after eval removal - -- [ ] **Stage SEC2: Template Injection Prevention** (Day 3-4) **[Luis]** - - [ ] Code: Secure template rendering - - [ ] **SEC2.1** [Luis] Replace `str.format()` with safe template engine: - - [ ] Use Jinja2 with sandboxed environment - - [ ] Or: restrict token set severely (only `{variable}` substitution) - - [ ] Prevent access to `__class__`, `__globals__`, etc. - - [ ] **SEC2.2** [Luis] Implement input sanitization for prompts: - - [ ] Treat user input as data, not instruction overrides - - [ ] Escape special characters in user-provided text - - [ ] Strict role separation in prompts (system vs user) - - [ ] **SEC2.3** [Luis] Add prompt injection mitigations: - - [ ] Detect common injection patterns - - [ ] Warn or reject suspicious inputs - - [ ] Log potential injection attempts - - [ ] Tests: Template security tests - - [ ] **SEC2.4** [Rui] Write Behave scenarios in `features/security_templates.feature`: - - [ ] Scenario: Template with Jinja2 injection attempt fails safely - - [ ] Scenario: User input with special characters is escaped - - [ ] Scenario: Prompt injection attempt is detected - -- [ ] **Stage SEC3: Exception Handling Audit** (Day 4-5) **[Luis]** - - [ ] Code: Stop swallowing exceptions (automated checks + manual fixes) - - [ ] **SEC3.1** [Luis] Run automated exception handling audit: - - [ ] Use semgrep rules to find bare `except:` or `except Exception:` - - [ ] Use vulture to find unreachable exception handlers - - [ ] Document each occurrence for manual review - - [ ] **SEC3.2** [Luis] Fix silent exception handling: - - [ ] Capture exception details - - [ ] Attach to message metadata or error state - - [ ] Fail the stream/plan with clear error state - - [ ] Log at appropriate level (error, not debug) - - [ ] **SEC3.3** [Luis] Add error context propagation: - - [ ] Errors should include stack trace reference - - [ ] Errors should identify the component that failed - - [ ] Errors should suggest recovery actions where possible - - [ ] **SEC3.4** [Brent] Review complex exception handling patterns only: - - [ ] Review async exception handling edge cases - - [ ] Validate error propagation across actor boundaries - - [ ] Tests: Exception handling tests - - [ ] **SEC3.5** [Rui] Write Behave scenarios in `features/security_exceptions.feature`: - - [ ] Scenario: Component failure surfaces as clear error - - [ ] Scenario: Error includes actionable information - - [ ] Scenario: No silent failures in normal operation - -- [ ] **Stage SEC4: Async Lifecycle Correctness** (Day 5-6) **[Luis]** - - [ ] Code: Fix async resource leaks (automated detection + fixes) - - [ ] **SEC4.1** [Luis] Run automated async pattern detection: - - [ ] Use semgrep to find all `asyncio.new_event_loop()` calls - - [ ] Use custom linter to detect unclosed resources - - [ ] Generate report of potential resource leaks - - [ ] **SEC4.2** [Luis] Fix RxPy subscription leaks: - - [ ] Ensure subscriptions are disposed on shutdown - - [ ] Dispose subscriptions on stream reconfiguration - - [ ] Track active subscriptions for debugging - - [ ] **SEC4.3** [Luis] Fix LangGraph checkpoint file leaks: - - [ ] Audit checkpoint file creation - - [ ] Implement cleanup for old checkpoint files - - [ ] Add retention policy for checkpoints - - [ ] **SEC4.4** [Brent] Selective review of async patterns: - - [ ] Review only complex async state machines - - [ ] Validate concurrent access patterns - - [ ] Tests: Resource leak tests - - [ ] **SEC4.5** [Rui] Write Behave scenarios in `features/security_async.feature`: - - [ ] Scenario: Long-running process does not leak memory - - [ ] Scenario: Shutdown cleans up all subscriptions - - [ ] Scenario: Checkpoint files cleaned after retention period - -- [ ] **Stage SEC5: Secrets Management** (Day 6-7) **[Hamza]** - - [ ] Code: Secure credential handling - - [ ] **SEC5.1** [Hamza] Implement secrets masking in logs: - - [ ] Detect API keys in log output - - [ ] Mask sensitive values (show only last 4 chars) - - [ ] Never log full credentials - - [ ] **SEC5.2** [Hamza] Secure environment variable handling: - - [ ] Validate API key format before use - - [ ] Clear error if required key missing - - [ ] Support secrets from file (for containerized environments) - - [ ] **SEC5.3** [Hamza] Prevent secrets in generated code: - - [ ] Detect hardcoded API keys in LLM output - - [ ] Warn if generated code contains potential secrets - - [ ] Block apply if secrets detected without override - - [ ] Tests: Secrets management tests - - [ ] **SEC5.4** [Rui] Write Behave scenarios in `features/security_secrets.feature`: - - [ ] Scenario: API key in log output is masked - - [ ] Scenario: Generated code with hardcoded key is flagged - - [ ] Scenario: Missing API key produces clear error - -- [ ] **Stage SEC6: Read-Only Action Enforcement** (Day 7-8) **[Luis]** - - [ ] Code: Enforce read_only actions - - [ ] **SEC6.1** [Luis] Add skill metadata validation at execution time: - - [ ] Check if action has `read_only: true` - - [ ] If read_only, verify all skills have `read_only: true` metadata - - [ ] Block execution if write skill detected in read-only action - - [ ] **SEC6.2** [Luis] Implement safety profile validation (DEFERRED to post-30; see Stage POST1): - - [ ] Action can specify `safety_profile` with: - - [ ] `allowed_skill_categories: list[str] | None` - - [ ] `denied_skill_categories: list[str] | None` - - [ ] `require_checkpoints: bool` - - [ ] `require_sandbox: bool` - - [ ] `require_human_approval: bool` (apply approval gate) - - [ ] `max_cost_usd: float | None` - - [ ] `max_retries: int` - - [ ] Add action-create CLI flags to populate SafetyProfile: - - [ ] `--require-sandbox` - - [ ] `--require-checkpoints` - - [ ] `--require-apply-approval` - - [ ] `--allow-skill-category ` (repeatable) - - [ ] `--deny-skill-category ` (repeatable) - - [ ] `--max-cost-usd ` - - [ ] `--max-retries ` - - [ ] Enforce safety profile at execution time - - [ ] Tests: Safety enforcement tests - - [ ] **SEC6.3** [Rui] Write Behave scenarios in `features/security_readonly.feature`: - - [ ] Scenario: Read-only action blocked from using write skill - - [ ] Scenario: Safety profile with require_sandbox enforced - - [ ] Scenario: Safety profile with require_checkpoints enforced - -- [ ] **Stage SEC7: Audit Logging** (Day 8-9) **[Hamza]** - - [ ] Code: Comprehensive audit trail - - [ ] **SEC7.1** [Hamza] Implement apply audit logging: - - [ ] Record who applied, what changed, when, why - - [ ] Include plan ID, action ID, project ID - - [ ] Include changeset summary (files affected) - - [ ] Store in audit table with retention policy - - [ ] **SEC7.2** [Hamza] Create Alembic migration for `audit_log` table: - - [ ] Schema: `audit_id`, `event_type`, `user_id`, `plan_id`, `details`, `created_at` - - [ ] Index on `event_type`, `plan_id`, `created_at` - - [ ] **SEC7.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] audit list` CLI command: - - [ ] List recent audit events - - [ ] Filter by event type, plan, date range - - [ ] Tests: Audit logging tests - - [ ] **SEC7.4** [Rui] Write Behave scenarios in `features/security_audit.feature`: - - [ ] Scenario: Apply creates audit log entry - - [ ] Scenario: Audit log queryable by plan ID - - [ ] Scenario: Audit list CLI shows recent events - ---- +**Parallel Group SEC7: Audit Logging [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: SEC7.audit) - Commit message: "feat(security): add audit logging for apply"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Add audit log model, migration, and `agents audit list` CLI command. + - [ ] Docs [Hamza]: Add `docs/reference/audit_logging.md`. + - [ ] Tests (Behave) [Rui]: Add `features/security_audit.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add audit logging integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_audit_bench.py` for log write overhead baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(security): add audit logging for apply"`. ### Section 12: Session & Provider Fixes [WORKSTREAM G - Hamza] **Target: Days 8-12** -- [ ] **Stage SESS1: Session Management** (Day 8-9) **[Hamza]** - - [ ] Code: Implement stable session persistence - - [ ] **SESS1.1** [Hamza] Define `Session` model in `src/cleveragents/domain/models/core/session.py`: - - [ ] Field `session_id: str` - ULID identifier - - [ ] Field `user_id: str | None` - optional user identity - - [ ] Field `automation_level: AutomationLevel` - session-level setting - - [ ] Field `current_plan_id: str | None` - active plan - - [ ] Field `plan_history: list[str]` - recent plan IDs - - [ ] Field `created_at: datetime` - - [ ] Field `last_active_at: datetime` - - [ ] Field `metadata: dict[str, Any]` - extensible metadata - - [ ] **SESS1.2** [Hamza] Create `SessionService` in `src/cleveragents/application/services/session_service.py`: - - [ ] Method `create_session() -> Session` - create new session with ULID - - [ ] Method `get_session(session_id: str) -> Session | None` - - [ ] Method `get_or_create_session() -> Session` - resume or create - - [ ] Method `update_activity(session_id: str) -> None` - touch last_active_at - - [ ] Method `set_current_plan(session_id: str, plan_id: str) -> None` - - [ ] Method `set_automation_level(session_id: str, level: AutomationLevel) -> None` - - [ ] **SESS1.3** [Hamza] Create Alembic migration for `sessions` table: - - [ ] Schema matching Session model - - [ ] Index on `last_active_at` for cleanup queries - - [ ] **SESS1.4** [Hamza] Implement session persistence across CLI invocations: - - [ ] Store session ID in `~/.cleveragents/session` - - [ ] Resume session on CLI startup if exists - - [ ] Clear session file on `agents [--data-dir PATH] [--config-path PATH] session end` command - - [ ] **SESS1.5** [Hamza] Add session CLI commands: - - [ ] `agents [--data-dir PATH] [--config-path PATH] session start` - create new session explicitly - - [ ] `agents [--data-dir PATH] [--config-path PATH] session end` - end current session - - [ ] `agents [--data-dir PATH] [--config-path PATH] session set automation-level ` - set session automation - - [ ] `agents [--data-dir PATH] [--config-path PATH] session info` - show current session details - - [ ] `agents [--data-dir PATH] [--config-path PATH] session tell "" [--session ] [--actor ]` - send a prompt in the active session - - [ ] Tests: Session tests - - [ ] **SESS1.6** [Rui] Write Behave scenarios in `features/session_management.feature`: - - [ ] Scenario: Session persists across CLI invocations - - [ ] Scenario: Session automation level overrides global - - [ ] Scenario: Session end clears session state - - [ ] Scenario: New CLI invocation resumes existing session - - [ ] Scenario: `agents [--data-dir PATH] [--config-path PATH] session tell "..."` uses current session and actor +**Parallel Group SESS1: Session Management [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: SESS1.session) - Commit message: "feat(session): add session model and CLI"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement Session model (session_id ULID, actor_name, title, created_at, updated_at) and persistence table `sessions`. + - [ ] Code [Hamza]: Implement SessionService with create/list/show/delete/export/import/tell operations per spec. + - [ ] Code [Hamza]: Implement CLI commands `session create/list/show/delete/export/import/tell` with rich/plain/json output. + - [ ] Docs [Hamza]: Add `docs/reference/session_management.md` with CLI examples and output fields. + - [ ] Tests (Behave) [Rui]: Add `features/session_management.feature` scenarios for create/list/show/delete/export/import/tell. + - [ ] Tests (Robot) [Rui]: Add session CLI smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/session_cli_bench.py` for session command overhead. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(session): add session model and CLI"`. -- [ ] **Stage SESS2: Memory Service Persistence** (Day 9-10) **[Hamza]** - - [ ] Code: Fix memory loss between invocations - - [ ] **SESS2.1** [Hamza] Update `MemoryService` to use session-based storage: - - [ ] Store conversation history keyed by session_id - - [ ] Load history on session resume - - [ ] Support configurable history limits - - [ ] **SESS2.2** [Hamza] Create Alembic migration for `conversation_history` table: - - [ ] Schema: `history_id`, `session_id`, `plan_id`, `role`, `content`, `created_at` - - [ ] Foreign key to sessions - - [ ] Index on `session_id`, `plan_id` - - [ ] **SESS2.3** [Hamza] Add explicit memory configuration: - - [ ] `CLEVERAGENTS_MEMORY_BACKEND=sqlite|redis|memory` - - [ ] Document that `memory` backend loses history between invocations - - [ ] Default to `sqlite` for persistence - - [ ] **SESS2.4** [Hamza] Surface warning if memory not persistent: - - [ ] On first CLI invocation, warn if memory backend is `memory` - - [ ] Suggest configuring persistent backend - - [ ] Tests: Memory persistence tests - - [ ] **SESS2.5** [Rui] Write Behave scenarios in `features/memory_persistence.feature`: - - [ ] Scenario: Conversation history survives CLI restart - - [ ] Scenario: Memory backend warning shown for in-memory mode - - [ ] Scenario: Plan-specific memory isolated from other plans +**Parallel Group SESS2: Memory Persistence [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: SESS2.memory) - Commit message: "feat(memory): persist session history"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Persist MemoryService history keyed by session_id with backend config and retention limits. + - [ ] Code [Hamza]: Add `session_messages` table (session_id, role, content, created_at) and indexing for recent retrieval. + - [ ] Docs [Hamza]: Document memory backend options, retention policy, and export/import behavior. + - [ ] Tests (Behave) [Rui]: Add `features/memory_persistence.feature` scenarios for save/load/trim. + - [ ] Tests (Robot) [Rui]: Add memory persistence integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/memory_persistence_bench.py` for storage overhead baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(memory): persist session history"`. -- [ ] **Stage PROV1: Provider Fixes** (Day 10-11) **[Luis]** - - [ ] Code: Fix provider issues - - [ ] **PROV1.1** [Luis] Remove FakeListLLM as default behavior: - - [ ] Audit where FakeListLLM is used outside tests - - [ ] Ensure production code never falls back to FakeListLLM - - [ ] If no provider configured, fail fast with clear message: - ``` - Error: No LLM provider configured. - Set OPENAI_API_KEY, ANTHROPIC_API_KEY, or configure an actor. - See: agents [--data-dir PATH] [--config-path PATH] help providers - ``` - - [ ] **PROV1.2** [Luis] Fix auto-debug hardcoded provider: - - [ ] Auto-debug currently hardcoded to OpenAI GPT-4 - - [ ] Change to use configured default actor - - [ ] Or: use action/actor system (auto-debug is just an action) - - [ ] **PROV1.3** [Luis] Verify OpenRouter implementation: - - [ ] OpenRouter listed in spec but may not be fully implemented - - [ ] Test OpenRouter adapter with real API - - [ ] Fix any issues found - - [ ] **PROV1.4** [Luis] Implement provider auto-detection: - - [ ] On startup, detect which API keys are configured - - [ ] Register only available providers - - [ ] Clear message about which providers are available: - ``` - Available providers: openai (gpt-4, gpt-3.5-turbo), anthropic (claude-3-opus) - Missing: google (GEMINI_API_KEY not set) - ``` - - [ ] Tests: Provider tests - - [ ] **PROV1.5** [Rui] Write Behave scenarios in `features/provider_fixes.feature`: - - [ ] Scenario: No provider configured produces clear error - - [ ] Scenario: Auto-debug uses configured actor not hardcoded - - [ ] Scenario: Provider detection shows available providers - - [ ] Scenario: FakeListLLM only used in test mode +**Parallel Group PROV1: Provider Fixes [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: PROV1.fixes) - Commit message: "fix(provider): remove FakeListLLM defaults"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Remove FakeListLLM fallback, fix auto-debug provider usage, and implement provider auto-detection. + - [ ] Code [Luis]: Update settings validation to fail fast when no providers are configured and no mock flag is set. + - [ ] Docs [Luis]: Update provider configuration docs and error messages. + - [ ] Tests (Behave) [Rui]: Add `features/provider_fixes.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add provider detection smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/provider_selection_bench.py` for provider resolution baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "fix(provider): remove FakeListLLM defaults"`. -- [ ] **Stage PROV2: Provider Fallback & Cost Controls** (Day 11-12) **[Luis]** - - [ ] Code: Implement cost controls and fallback - - [ ] **PROV2.1** [Luis] Add token tracking per plan: - - [ ] Track input tokens, output tokens per LLM call - - [ ] Aggregate by plan, phase, actor - - [ ] Store in plan metadata - - [ ] **PROV2.2** [Luis] Add cost estimation: - - [ ] Map model to token cost ($ per 1K tokens) - - [ ] Calculate estimated cost per call - - [ ] Aggregate plan total cost - - [ ] **PROV2.3** [Luis] Implement budget limits: - - [ ] Per-plan max cost limit - - [ ] Per-session max cost limit - - [ ] Global max cost limit - - [ ] Warn when approaching limit, fail when exceeded - - [ ] **PROV2.4** [Luis] Implement rate limiting: - - [ ] Per-actor max calls per minute - - [ ] Per-plan max retries - - [ ] Exponential backoff on rate limit errors - - [ ] **PROV2.5** [Luis] Implement provider fallback: - - [ ] If primary provider fails, try fallback provider - - [ ] Configurable fallback order - - [ ] Log fallback events - - [ ] Tests: Cost control tests - - [ ] **PROV2.6** [Rui] Write Behave scenarios in `features/cost_controls.feature`: - - [ ] Scenario: Plan cost tracked and reported - - [ ] Scenario: Plan exceeding budget limit fails - - [ ] Scenario: Rate limit triggers exponential backoff - - [ ] Scenario: Provider fallback on transient failure +**Parallel Group PROV2: Cost Controls & Fallback [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: PROV2.costs) - Commit message: "feat(provider): add cost controls and fallback"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Track tokens/costs, enforce budgets, rate limits, and provider fallback order. + - [ ] Code [Luis]: Add cost tracking fields to plan execution metadata and surface in `plan status`. + - [ ] Docs [Luis]: Add `docs/reference/cost_controls.md` with config keys and thresholds. + - [ ] Tests (Behave) [Rui]: Add `features/cost_controls.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add cost control integration smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/cost_controls_bench.py` for cost check overhead. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "feat(provider): add cost controls and fallback"`. --- ### Section 13: Additional CLI Commands & UX [Days 10-14] -**Commands missing from initial plan** +**Parallel Group CLI0: Core System Commands [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: CLI0.core) - Commit message: "feat(cli): add version/info/diagnostics"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement `version`, `info`, and `diagnostics` commands with rich/plain/json/yaml output parity. + - [ ] Code [Hamza]: Add diagnostics checks for config file, database, providers, and filesystem permissions per spec. + - [ ] Docs [Hamza]: Update CLI reference with core system commands and sample outputs. + - [ ] Tests (Behave) [Rui]: Add `features/cli_core.feature` scenarios for each command output. + - [ ] Tests (Robot) [Rui]: Add core command smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/cli_core_bench.py` for command runtime baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(cli): add version/info/diagnostics"`. -- [ ] **Stage CLI0: Core System Commands** (Day 10) **[Hamza]** - - [ ] Code: Implement core CLI metadata commands - - [ ] **CLI0.1** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] version`: - - [ ] Print CLI version and build metadata - - [ ] Include config path and data dir in verbose output - - [ ] **CLI0.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] info`: - - [ ] Show configuration summary (data dir, config path, providers, defaults) - - [ ] Show current session if present - - [ ] **CLI0.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] diagnostics`: - - [ ] Run self-checks (config readable, DB reachable, write access) - - [ ] Print warnings for missing providers or invalid config - - [ ] Tests: Core CLI command tests - - [ ] **CLI0.4** [Rui] Write Behave scenarios in `features/cli_core.feature`: - - [ ] Scenario: Version prints semantic version - - [ ] Scenario: Info prints config and data paths - - [ ] Scenario: Diagnostics reports missing providers +**Parallel Group CLI1: Plan Interaction Commands [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: CLI1.plan) - Commit message: "feat(cli): add plan prompt/diff/artifacts"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement `plan prompt`, `plan diff`, and `plan artifacts` commands. + - [ ] Code [Hamza]: Ensure `plan diff` supports `--format` output and includes validation summary. + - [ ] Docs [Hamza]: Update CLI reference with plan interaction commands and output formats. + - [ ] Tests (Behave) [Rui]: Add `features/plan_interaction_cli.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add plan interaction CLI smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_cli_interaction_bench.py` for diff/artifacts runtime. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(cli): add plan prompt/diff/artifacts"`. -- [ ] **Stage CLI1: Plan Interaction Commands** (Day 10-11) **[Hamza]** - - [ ] Code: Implement additional plan commands - - [ ] **CLI1.1** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan prompt ""`: - - [ ] Provide additional instructions to a stuck plan - - [ ] Works when plan is in errored state - - [ ] Resumes execution with new guidance - - [ ] **CLI1.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan diff `: - - [ ] Show diff of changes made by plan - - [ ] Works for plans in Execute or Apply phase - - [ ] Color-coded unified diff output - - [ ] **CLI1.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan diff --correction `: - - [ ] Compare old vs new after correction - - [ ] Show what changed between correction attempts - - [ ] **CLI1.4** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] plan artifacts `: - - [ ] List all artifacts produced by plan - - [ ] Show file paths, operation types, sizes - - [ ] Tests: Plan interaction CLI tests - - [ ] **CLI1.5** [Rui] Write Behave scenarios in `features/plan_interaction_cli.feature`: - - [ ] Scenario: Plan prompt resumes stuck plan - - [ ] Scenario: Plan diff shows color-coded output - - [ ] Scenario: Correction diff comparison works +**Parallel Group CLI2: Configuration Commands [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: CLI2.config) - Commit message: "feat(cli): add config and provider commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement `config set/get/list` and `providers list` commands. + - [ ] Code [Hamza]: Support `config list` regex filtering and `--filter-values` per spec. + - [ ] Docs [Hamza]: Update CLI reference with configuration commands and filtering examples. + - [ ] Tests (Behave) [Rui]: Add `features/config_cli.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add config CLI smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/config_cli_bench.py` for command parsing baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(cli): add config and provider commands"`. -- [ ] **Stage CLI2: Configuration Commands** (Day 11-12) **[Hamza]** - - [ ] Code: Implement config commands - - [ ] **CLI2.1** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] config set `: - - [ ] Set global configuration values - - [ ] Supported keys: `automation-level`, `default-actor`, `log-level` - - [ ] Persist to `~/.cleveragents/config.toml` - - [ ] **CLI2.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] config get `: - - [ ] Get current configuration value - - [ ] Show source (default, file, environment) - - [ ] **CLI2.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] config list`: - - [ ] List all configuration values - - [ ] Show current value and source - - [ ] **CLI2.4** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] providers list`: - - [ ] List available providers - - [ ] Show which are configured vs missing API keys - - [ ] Tests: Config CLI tests - - [ ] **CLI2.5** [Rui] Write Behave scenarios in `features/config_cli.feature`: - - [ ] Scenario: Config set persists value - - [ ] Scenario: Config get shows current value - - [ ] Scenario: Providers list shows available/missing - -- [ ] **Stage CLI3: Context Commands** (Day 12-13) **[Hamza]** - - [ ] Code: Implement project/actor context policy commands - - [ ] **CLI3.1** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project context set`: - - [ ] Flags: `--project `, `--policy hot|warm|cold`, `--hot-max-tokens ` - - [ ] `--hot-max-tokens` is a soft cap; allow null/omitted to disable - - [ ] LLM hard context limit can override soft cap - - [ ] **CLI3.2** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] project context show`: - - [ ] Display current policy and hot/warm/cold sizing - - [ ] **CLI3.3** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] actor context set`: - - [ ] Reuse same arguments/behavior as project context set and legacy context commands - - [ ] **CLI3.4** [Hamza] Implement `agents [--data-dir PATH] [--config-path PATH] actor context show`: - - [ ] Reuse same output format as project context show - - [ ] Tests: Context command tests - - [ ] **CLI3.5** [Rui] Write Behave scenarios in `features/context_cli.feature`: - - [ ] Scenario: Project context set updates policy - - [ ] Scenario: Project context show displays hot/warm/cold tiers - - [ ] Scenario: Actor context set mirrors project context args - - [ ] Scenario: Actor context show displays policy summary +**Parallel Group CLI3: Context Commands [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: CLI3.context) - Commit message: "feat(cli): add context policy commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement `project context set/show` and `actor context set/show` commands. + - [ ] Code [Hamza]: Support include/exclude resource and path globs, token limits, and summarize flags per spec. + - [ ] Docs [Hamza]: Update CLI reference with context policy usage and examples. + - [ ] Tests (Behave) [Rui]: Add `features/context_cli.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add context CLI smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/context_cli_bench.py` for command runtime baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(cli): add context policy commands"`. --- ### Section 14: Concurrency & Cleanup [Days 12-14] -- [ ] **Stage CONC1: Plan Locking** (Day 12) **[Luis]** - - [ ] Code: Prevent concurrent plan modification - - [ ] **CONC1.1** [Luis] Implement plan-level locking: - - [ ] Database row lock on plan during execution - - [ ] Or: advisory lock using plan_id - - [ ] Prevent two processes from executing same plan - - [ ] **CONC1.2** [Luis] Implement project-level locking: - - [ ] Lock project during apply (can't apply two plans to same project) - - [ ] Allow parallel plans in different sandboxes - - [ ] **CONC1.3** [Luis] Add lock timeout and retry: - - [ ] Configurable lock wait timeout - - [ ] Clear error if lock cannot be acquired - - [ ] Tests: Concurrency tests - - [ ] **CONC1.4** [Rui] Write Behave scenarios in `features/concurrency.feature`: - - [ ] Scenario: Two processes cannot execute same plan - - [ ] Scenario: Two applies to same project blocked - - [ ] Scenario: Lock timeout produces clear error +**Parallel Group CONC1: Plan Locking [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: CONC1.lock) - Commit message: "feat(concurrency): add plan and project locks"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Implement plan-level and project-level locks with timeouts. + - [ ] Code [Luis]: Add `locks` table with owner_id, resource_type, resource_id, acquired_at, expires_at. + - [ ] Code [Luis]: Ensure locks are enforced in PlanLifecycleService transitions and SubplanService scheduling. + - [ ] Docs [Luis]: Add `docs/reference/concurrency.md` with lock behavior. + - [ ] Tests (Behave) [Rui]: Add `features/concurrency.feature` scenarios for lock contention and expiry. + - [ ] Tests (Robot) [Rui]: Add lock integration smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/concurrency_lock_bench.py` for lock overhead baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "feat(concurrency): add plan and project locks"`. -- [ ] **Stage CONC2: Resumable Execution** (Day 12-13) **[Luis]** - - [ ] Code: Implement plan resume capability - - [ ] **CONC2.1** [Luis] Persist step-level progress: - - [ ] Record each completed step in plan - - [ ] Store intermediate state for resume - - [ ] **CONC2.2** [Luis] Implement `agents [--data-dir PATH] [--config-path PATH] plan resume `: - - [ ] Detect where plan was interrupted - - [ ] Resume from last completed step - - [ ] Restore sandbox state - - [ ] **CONC2.3** [Luis] Handle graceful shutdown: - - [ ] On SIGINT/SIGTERM, save progress before exit - - [ ] Mark plan as "interrupted" not "errored" - - [ ] Tests: Resume tests - - [ ] **CONC2.4** [Rui] Write Behave scenarios in `features/plan_resume.feature`: - - [ ] Scenario: Interrupted plan can be resumed - - [ ] Scenario: Resume continues from correct step - - [ ] Scenario: Graceful shutdown saves progress +**Parallel Group CONC2: Resumable Execution [Luis]** +- [ ] **COMMIT (Owner: Luis | Group: CONC2.resume) - Commit message: "feat(concurrency): add plan resume"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Luis]: Persist step-level progress and implement `plan resume` with graceful shutdown handling. + - [ ] Code [Luis]: Add resume checkpoints tied to decision IDs and sandbox checkpoints. + - [ ] Docs [Luis]: Update plan lifecycle docs for resume behavior. + - [ ] Tests (Behave) [Rui]: Add `features/plan_resume.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add resume integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_resume_bench.py` for resume overhead baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "feat(concurrency): add plan resume"`. -- [ ] **Stage CONC3: Garbage Collection** (Day 13-14) **[Hamza]** - - [ ] Code: Cleanup abandoned resources - - [ ] **CONC3.1** [Hamza] Implement sandbox garbage collection: - - [ ] On startup, find orphaned sandbox directories - - [ ] Clean sandboxes from crashed processes - - [ ] Add `agents [--data-dir PATH] [--config-path PATH] cleanup sandboxes` CLI command - - [ ] **CONC3.2** [Hamza] Implement checkpoint file cleanup: - - [ ] Track checkpoint files in database - - [ ] Retention policy (default: 7 days after plan completion) - - [ ] Add `agents [--data-dir PATH] [--config-path PATH] cleanup checkpoints` CLI command - - [ ] **CONC3.3** [Hamza] Implement session cleanup: - - [ ] Clean sessions with no activity for 30 days - - [ ] Clean associated conversation history - - [ ] **CONC3.4** [Hamza] Add automatic cleanup on startup: - - [ ] Run cleanup for orphaned resources - - [ ] Log what was cleaned - - [ ] **CONC3.5** [Brent] Review resource lifecycle patterns only: - - [ ] Validate cleanup doesn't affect active resources - - [ ] Review concurrent access during cleanup - - [ ] Tests: Cleanup tests - - [ ] **CONC3.6** [Rui] Write Behave scenarios in `features/garbage_collection.feature`: - - [ ] Scenario: Orphaned sandbox cleaned on startup - - [ ] Scenario: Old checkpoints cleaned by retention policy - - [ ] Scenario: Cleanup commands work manually - ---- - -### Section 15: Definition of Done & Invariants [Days 14-15] - -- [ ] **Stage DOD1: Definition of Done Enforcement** (Day 14) **[Luis]** - - [ ] Code: Implement DoD validation - - [ ] **DOD1.1** [Luis] Parse DoD must/should/may structure: - - [ ] Parse action's `definition_of_done` field - - [ ] Identify MUST, SHOULD, MAY requirements - - [ ] Generate validation checklist from DoD - - [ ] **DOD1.2** [Luis] Validate DoD after execution: - - [ ] Run DoD checklist after Execute completes - - [ ] MUST requirements block apply if failed - - [ ] SHOULD requirements warn but allow apply - - [ ] MAY requirements are informational only - - [ ] **DOD1.3** [Luis] Display DoD validation results: - - [ ] Show checklist in CLI output - - [ ] Color-code: green (pass), red (fail), yellow (warn) - - [ ] Tests: DoD tests - - [ ] **DOD1.4** [Rui] Write Behave scenarios in `features/definition_of_done.feature`: - - [ ] Scenario: DoD MUST failure blocks apply - - [ ] Scenario: DoD SHOULD failure warns but allows apply - - [ ] Scenario: DoD validation results displayed - -- [ ] **Stage DOD2: Invariant System** (Day 14-15) **[Luis]** - - [ ] Code: Implement user-defined invariants - - [ ] **DOD2.1** [Luis] Define `Invariant` model: - - [ ] Field `invariant_id: str` - - [ ] Field `project_id: str` - - [ ] Field `description: str` - human readable - - [ ] Field `check_command: str | None` - shell command to verify - - [ ] Field `check_code: str | None` - Python code to verify - - [ ] Field `severity: str` - error|warning - - [ ] **DOD2.2** [Luis] Implement `agents [--data-dir PATH] [--config-path PATH] project add-invariant`: - - [ ] Add invariant to project - - [ ] Specify check command or code - - [ ] **DOD2.3** [Luis] Check invariants during execution: - - [ ] Run invariant checks after each major step - - [ ] Fail execution if invariant violated (severity=error) - - [ ] Warn if invariant violated (severity=warning) - - [ ] Tests: Invariant tests - - [ ] **DOD2.4** [Rui] Write Behave scenarios in `features/invariants.feature`: - - [ ] Scenario: Invariant violation blocks execution - - [ ] Scenario: Invariant warning allows continuation - - [ ] Scenario: Invariant with command check works +**Parallel Group CONC3: Garbage Collection [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: CONC3.gc) - Commit message: "feat(ops): add cleanup commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Add cleanup for sandboxes, checkpoints, and stale sessions with CLI commands. + - [ ] Code [Hamza]: Add retention policy settings for sandbox age, checkpoint count, and session inactivity. + - [ ] Docs [Hamza]: Document cleanup commands and retention defaults. + - [ ] Tests (Behave) [Rui]: Add `features/garbage_collection.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add cleanup integration smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/cleanup_bench.py` for cleanup overhead baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(ops): add cleanup commands"`. --- ### Section 16: Context Indexing [Days 15-17] -- [ ] **Stage CTX1: Repository Indexing** (Day 15-16) **[Hamza]** - - [ ] Code: Implement repo indexing for large codebases - - [ ] **CTX1.1** [Hamza] Create `IndexingService` in `src/cleveragents/application/services/indexing_service.py`: - - [ ] Method `index_project(project: Project) -> Index`: - - [ ] Scan all files in project resources - - [ ] Apply ignore patterns - - [ ] Build file tree index - - [ ] Detect language per file - - [ ] Method `search(query: str, project_id: str) -> list[SearchResult]`: - - [ ] Full-text search across indexed files - - [ ] Return file paths, line numbers, snippets - - [ ] Method `refresh_index(project_id: str) -> None`: - - [ ] Update index for changed files only - - [ ] **CTX1.2** [Hamza] Implement file tree representation: - - [ ] Parse directory structure - - [ ] Include file sizes, modification times - - [ ] Support efficient subtree queries - - [ ] **CTX1.3** [Hamza] Add language detection: - - [ ] Detect language from file extension - - [ ] Detect language from shebang/magic bytes - - [ ] Store language in index - - [ ] Tests: Indexing tests - - [ ] **CTX1.4** [Rui] Write Behave scenarios in `features/context_indexing.feature`: - - [ ] Scenario: Project indexing creates searchable index - - [ ] Scenario: Search returns relevant results - - [ ] Scenario: Index refresh updates changed files only +**Parallel Group CTX1: Repository Indexing [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: CTX1.index) - Commit message: "feat(context): add repo indexing service"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Implement indexing service with file tree, language detection, and incremental refresh. + - [ ] Code [Hamza]: Add index metadata table (resource_id, indexed_at, file_count, token_estimate). + - [ ] Code [Hamza]: Enforce max file size and total size limits from project context policy. + - [ ] Docs [Hamza]: Add `docs/reference/context_indexing.md`. + - [ ] Tests (Behave) [Rui]: Add `features/context_indexing.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add indexing integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/context_indexing_bench.py` for indexing throughput baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(context): add repo indexing service"`. -- [ ] **Stage CTX2: Embedding Index** (Day 16-17) **[Hamza]** - - [ ] Code: Optional embedding-based search - - [ ] **CTX2.1** [Hamza] Integrate with VectorStoreService: - - [ ] Chunk files into segments - - [ ] Generate embeddings for each chunk - - [ ] Store in FAISS index - - [ ] **CTX2.2** [Hamza] Implement semantic search: - - [ ] Method `semantic_search(query: str, project_id: str) -> list[SearchResult]` - - [ ] Use query embedding to find similar chunks - - [ ] Return ranked results with relevance scores - - [ ] **CTX2.3** [Hamza] Make embedding index optional: - - [ ] Only build if `CLEVERAGENTS_ENABLE_EMBEDDINGS=true` - - [ ] Fall back to full-text search if not available - - [ ] Tests: Embedding tests - - [ ] **CTX2.4** [Rui] Write Behave scenarios in `features/embedding_search.feature`: - - [ ] Scenario: Semantic search returns conceptually similar results - - [ ] Scenario: System works without embedding index +**Parallel Group CTX2: Embedding Index [Hamza]** +- [ ] **COMMIT (Owner: Hamza | Group: CTX2.embedding) - Commit message: "feat(context): add optional embedding search"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Add embedding-based search with opt-in flag and fallback to full-text search. + - [ ] Code [Hamza]: Add embedding index metadata and cache invalidation on repo updates. + - [ ] Docs [Hamza]: Add `docs/reference/embedding_search.md`. + - [ ] Tests (Behave) [Rui]: Add `features/embedding_search.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add embedding search integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/embedding_search_bench.py` for search runtime baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(context): add optional embedding search"`. --- ### Section 17: Skill Registry [Days 17-18] -- [ ] **Stage SKILL1: Skill Catalog** (Day 17-18) **[Aditya]** - - [ ] Code: Implement skill registry for safety validation - - [ ] **SKILL1.1** [Aditya] Create `SkillRegistry` in `src/cleveragents/actor/skills/registry.py`: - - [ ] Method `register_skill(skill: Skill) -> None` - - [ ] Method `get_skill(name: str) -> Skill | None` - - [ ] Method `list_skills() -> list[Skill]` - - [ ] Method `list_skills_by_capability(read_only: bool) -> list[Skill]` - - [ ] **SKILL1.2** [Aditya] Auto-register skills from actor configs: - - [ ] When actor config parsed, extract tool definitions - - [ ] Register each tool as a skill with metadata - - [ ] **SKILL1.3** [Aditya] Validate skill usage against plan requirements: - - [ ] Check skill capabilities against action requirements - - [ ] Fail if incompatible skill used - - [ ] **SKILL1.4** [Aditya] Implement `agents [--data-dir PATH] [--config-path PATH] skills list`: - - [ ] List all registered skills - - [ ] Show capabilities (read_only, checkpointable, etc.) - - [ ] Tests: Skill registry tests - - [ ] **SKILL1.5** [Rui] Write Behave scenarios in `features/skill_registry.feature`: - - [ ] Scenario: Skills auto-registered from actor config - - [ ] Scenario: Skill capability query works - - [ ] Scenario: Incompatible skill usage detected +**Parallel Group SKILL1: Skill Catalog [Aditya]** +- [ ] **COMMIT (Owner: Aditya | Group: SKILL1.registry) - Commit message: "feat(skill): add skill registry and CLI"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Aditya]: Implement SkillRegistry, auto-registration from actor configs, and `skills list` CLI. + - [ ] Code [Aditya]: Add skill YAML schema loader for `agents skill add` (namespaced name + tool refs). + - [ ] Code [Aditya]: Implement `agents skill show` and `agents skill tools` outputs per spec. + - [ ] Docs [Aditya]: Add `docs/reference/skill_registry.md`. + - [ ] Tests (Behave) [Rui]: Add `features/skill_registry.feature` scenarios. + - [ ] Tests (Robot) [Rui]: Add skill registry integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/skill_registry_bench.py` for registry lookup baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Aditya]: `git commit -m "feat(skill): add skill registry and CLI"`. --- @@ -5282,31 +3021,111 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is The following items are deferred or no longer applicable: -- [ ] **Deferred: REPL Mode** - Focus on CLI first, REPL is optional enhancement -- [ ] **Deferred: Auth/Team Commands** - Requires server connectivity (server is a separate project) -- [ ] **Deferred: TUI/Web Interface** - After CLI is complete -- [ ] **Deferred: Database Resources** - After source code resources work -- [ ] **Deferred: Cloud Infrastructure Resources** - After source code resources work -- [ ] **Deferred: Permission System** - Requires server connectivity (server is a separate project) - - [ ] Namespace-level permissions (who can create/edit org actions) - - [ ] Project-level permissions (who can modify resources, apply changes) - - [ ] Plan-level permissions (can this plan write, require approvals) - - [ ] Skill-level permissions (require approval per call or elevated role) -- [ ] **POST1: Safety Profile Enforcement (Post-30)** - Deferred safety policy system - - [ ] Move `safety_profile` into Action model (from Stage A2 follow-up) - - [ ] Define `SafetyProfile` model with allow/deny skill categories and approval gates - - [ ] Add action-create CLI flags for safety profile: - - [ ] `--require-sandbox` - - [ ] `--require-checkpoints` - - [ ] `--require-apply-approval` - - [ ] `--allow-skill-category ` (repeatable) - - [ ] `--deny-skill-category ` (repeatable) - - [ ] `--max-cost-usd ` - - [ ] `--max-retries ` - - [ ] Enforce safety profile during execution and apply - - [ ] Add Behave + Robot tests for safety profile enforcement -- [ ] **Removed: Old 67-command structure** - Replaced by new command structure -- [ ] **Removed: Configuration migration utilities** - CleverAgents is standalone +- [ ] **COMMIT (Owner: Luis | Group: POST.server) - Commit message: "feat(client): add server http client"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add HTTP client with health check, version negotiation, and OpenAPI codegen integration. + - [ ] Docs [Luis]: Add `docs/reference/server_client_http.md` with configuration and connection errors. + - [ ] Tests (Behave) [Rui]: Add scenarios for connection errors and version mismatch handling. + - [ ] Tests (Robot) [Rui]: Add mock-server connection tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/server_http_client_bench.py` for connection overhead baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "feat(client): add server http client"`. + +- [ ] **COMMIT (Owner: Luis | Group: POST.server) - Commit message: "feat(client): add plan sync and remote execution"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Sync actions, request remote plan execution/apply/status, and reconcile remote plan IDs. + - [ ] Docs [Luis]: Document sync semantics and conflict handling in `docs/reference/server_sync.md`. + - [ ] Tests (Behave) [Rui]: Add scenarios for sync conflicts and retry behavior. + - [ ] Tests (Robot) [Rui]: Add mock-server sync tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/server_sync_bench.py` for sync throughput baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "feat(client): add plan sync and remote execution"`. + +- [ ] **COMMIT (Owner: Luis | Group: POST.server) - Commit message: "feat(client): add websocket updates"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add WebSocket client for plan updates with reconnect/backoff policy. + - [ ] Docs [Luis]: Add `docs/reference/server_websocket.md` with event types and reconnect rules. + - [ ] Tests (Behave) [Rui]: Add scenarios for reconnect and event ordering. + - [ ] Tests (Robot) [Rui]: Add WebSocket mock tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/server_ws_bench.py` for message handling baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Luis]: `git commit -m "feat(client): add websocket updates"`. + +- [ ] **COMMIT (Owner: Hamza | Group: POST.server) - Commit message: "feat(client): add remote project support"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Hamza]: Add remote resource selection and server execution request wiring. + - [ ] Docs [Hamza]: Add `docs/reference/server_remote_projects.md` with project selection semantics. + - [ ] Tests (Behave) [Rui]: Add scenarios for remote project selection errors. + - [ ] Tests (Robot) [Rui]: Add remote execution mock tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/server_remote_project_bench.py` for request overhead baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [Hamza]: `git commit -m "feat(client): add remote project support"`. + +- [ ] **COMMIT (Owner: TBD | Group: POST.repl) - Commit message: "feat(cli): add interactive repl"** + - [ ] Code [TBD]: Implement REPL command loop with history and completion. + - [ ] Code [TBD]: Add persistent history file under `~/.cleveragents/history` with opt-out flag. + - [ ] Docs [TBD]: Add REPL usage guide. + - [ ] Tests (Behave) [Rui]: Add REPL behavior scenarios. + - [ ] Tests (Robot) [Rui]: Add REPL smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/repl_bench.py` for REPL startup baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [TBD]: `git commit -m "feat(cli): add interactive repl"`. + +- [ ] **COMMIT (Owner: TBD | Group: POST.auth) - Commit message: "feat(cli): add auth and team commands"** + - [ ] Code [TBD]: Add auth/team CLI commands (requires server connectivity) with stubbed responses. + - [ ] Code [TBD]: Add config keys for auth token storage and team context. + - [ ] Docs [TBD]: Document auth/team workflows. + - [ ] Tests (Behave) [Rui]: Add auth/team CLI scenarios. + - [ ] Tests (Robot) [Rui]: Add auth/team integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/auth_cli_bench.py` for auth command baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [TBD]: `git commit -m "feat(cli): add auth and team commands"`. + +- [ ] **COMMIT (Owner: TBD | Group: POST.tui) - Commit message: "feat(ui): add TUI/Web interface"** + - [ ] Code [TBD]: Implement TUI/Web interfaces (client-only) with plan status, logs, and diff views. + - [ ] Code [TBD]: Add UI routing stub and data provider interface (local-only). + - [ ] Docs [TBD]: Add UI usage guide. + - [ ] Tests (Behave) [Rui]: Add UI behavior scenarios. + - [ ] Tests (Robot) [Rui]: Add UI smoke tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/ui_render_bench.py` for UI render baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [TBD]: `git commit -m "feat(ui): add TUI/Web interface"`. + +- [ ] **COMMIT (Owner: TBD | Group: POST.dbresources) - Commit message: "feat(resource): add database resources"** + - [ ] Code [TBD]: Add database resource types and sandbox strategy (transaction wrapper). + - [ ] Code [TBD]: Add resource type schema with connection parameters and auth handling. + - [ ] Docs [TBD]: Document database resource configuration. + - [ ] Tests (Behave) [Rui]: Add database resource scenarios. + - [ ] Tests (Robot) [Rui]: Add database resource integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_resource_bench.py` for resource registration baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [TBD]: `git commit -m "feat(resource): add database resources"`. + +- [ ] **COMMIT (Owner: TBD | Group: POST.cloud) - Commit message: "feat(resource): add cloud infrastructure resources"** + - [ ] Code [TBD]: Add cloud resource types and sandbox strategies (stubbed local-only). + - [ ] Code [TBD]: Add resource type schema with provider-specific credential fields. + - [ ] Docs [TBD]: Document cloud resource configuration. + - [ ] Tests (Behave) [Rui]: Add cloud resource scenarios. + - [ ] Tests (Robot) [Rui]: Add cloud resource integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/cloud_resource_bench.py` for resource registration baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [TBD]: `git commit -m "feat(resource): add cloud infrastructure resources"`. + +- [ ] **COMMIT (Owner: TBD | Group: POST.permissions) - Commit message: "feat(security): add permission system"** + - [ ] Code [TBD]: Implement namespace/project/plan/skill permission enforcement (requires server). + - [ ] Code [TBD]: Add permission model with role bindings and default deny rules. + - [ ] Docs [TBD]: Document permission model and roles. + - [ ] Tests (Behave) [Rui]: Add permission scenarios. + - [ ] Tests (Robot) [Rui]: Add permission integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/permission_check_bench.py` for enforcement baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [TBD]: `git commit -m "feat(security): add permission system"`. + +- [ ] **COMMIT (Owner: TBD | Group: POST.safety) - Commit message: "feat(security): add safety profile enforcement"** + - [ ] Code [TBD]: Add SafetyProfile model, CLI flags, and execution enforcement. + - [ ] Code [TBD]: Add safety profile resolution order (plan > project > global). + - [ ] Docs [TBD]: Document safety profile options and defaults. + - [ ] Tests (Behave) [Rui]: Add safety profile enforcement scenarios. + - [ ] Tests (Robot) [Rui]: Add safety profile integration tests. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/safety_profile_bench.py` for enforcement baseline. + - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Commit [TBD]: `git commit -m "feat(security): add safety profile enforcement"`. --- @@ -5327,13 +3146,13 @@ The following items are deferred or no longer applicable: ### Week 1 (Days 1-7) - MVP Target (Source Code Only) | Day | Morning Focus | Owner | Afternoon Focus | Owner | |-----|---------------|-------|-----------------|-------| -| 1 | A5.1-A5.4 Plan/Action DB Schema | Jeff + Luis | B1.1-B1.6 Project/Resource Models | Hamza | -| 2 | A5.5-A5.9 Plan/Action Repositories | Jeff | B2.1-B2.4 Project CLI Commands | Hamza + Rui (tests) | -| 3 | B3.1-B3.6 Sandbox Protocol + Git | Jeff + Hamza | B3.7-B3.13 Sandbox Manager + Tests | Luis + Rui | -| 4 | C1.1-C1.6 Actor YAML Schema | Aditya | C2.1-C2.8 Actor Compiler | Aditya + Jeff | -| 5 | C3.1-C3.5 Skill Protocol | Jeff | C3.6 Built-in File Skills | Luis + Jeff | -| 6 | C3.7 MCP Adapter | Aditya + Jeff | C4.1-C4.3 Change Tracking | Luis | -| 7 | C4.4-C4.7 Tool Router + Diff | Jeff | C5.1-C5.5 Validation Pipeline | Luis + Rui | +| 1 | A5.alpha + A5.action_arguments DB migrations + ORM models | Jeff + Luis | B1.core Project/Resource models | Hamza | +| 2 | A5.gamma repos/services + A5.tests + B3.cleanup (legacy project CLI) | Jeff | B2.persistence/B2.service + B3.cli Resource CLI | Hamza + Rui (tests) | +| 3 | B4.sandbox git_worktree | Jeff + Hamza | B4.sandbox manager + tests | Luis + Rui | +| 4 | C1.schema/C1.examples Actor YAML | Aditya | C2.loader/C2.compiler + C2.legacy v2 removal | Aditya + Jeff | +| 5 | C3.protocol/C3.context/C3.inline Skill framework | Jeff | C4.file/C4.search Built-in skills | Luis + Jeff | +| 6 | C7.mcp MCP Adapter | Aditya + Jeff | C4.git + C5.model/C5.router Change tracking | Luis + Jeff | +| 7 | C5.diff Diff review artifacts | Luis | C6.pipeline/C6.gating Validation pipeline | Luis + Rui | ### Week 2 (Days 8-14) - M3 Complete + Plan-Actor Integration | Day | Focus | Owner | Deliverable | @@ -5407,10 +3226,10 @@ The following items are deferred or no longer applicable: ### Critical Path Dependencies ``` -Day 1: A5 (Persistence) ────────────────────────────────────────────┐ +Day 1: A5 (Persistence + Action Args) ───────────────────────────────┐ Day 2: B1.core/B2.persistence/B2.service/B3.cli (Project/Resource) ─┐│ Day 3: B4.sandbox (Sandbox) ────────────────────────────────────────┼┤ -Day 4: C1.schema/C2.compiler (Actor) ───────────────────────────────┘│ +Day 4: C1.schema/C2.legacy/C2.compiler (Actor) ─────────────────────┘│ Day 5: C3.protocol/C4.file (Skills) ─────────────────────────────────┤ Day 6: C4.search/C5.model (Change Tracking) ─────────────────────────┤ Day 7: C6.pipeline/C7.mcp/C8.providers (Validation + Providers) ─────┘ @@ -5741,6 +3560,9 @@ DAY 4-7: ACTOR/SKILL LAYER │ [Aditya] C1.schema/C1.examples Actor Schema │ │ │ │ │ ▼ │ +│ [Jeff] C2.legacy Drop v2 actor configs │ +│ │ │ +│ ▼ │ │ [Aditya+Jeff] C2.loader/C2.compiler Actor Compiler │ │ │ │ │ [Jeff] C3.protocol/C3.context/C3.inline ═══► [Aditya] C7.mcp │ -- 2.52.0 From 93fe2c53e0924731ee1eb7b95613ab2302674255 Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Freeman Date: Wed, 11 Feb 2026 23:24:18 -0500 Subject: [PATCH 06/11] Docs: revampted implementation plan again --- implementation_plan.md | 2699 +++++++++++++++++++--------------------- 1 file changed, 1255 insertions(+), 1444 deletions(-) diff --git a/implementation_plan.md b/implementation_plan.md index f69aff782..b65212726 100644 --- a/implementation_plan.md +++ b/implementation_plan.md @@ -1,7 +1,6 @@ # CleverAgents Implementation Plan ## **CRITICAL**: Execute These Rules Without Exception - - **Strictly adhere to guidelines in `./CONTRIBUTING.md`**: All rules and guidelines outlined in this file must be strictly followed at all times. - **Python implementation scope only**: Every action described here pertains to building an idiomatic Python codebase that implements the CleverAgents architecture. - **NO BACKWARDS COMPATIBILITY**: CleverAgents is a NEW standalone project. Do NOT maintain any backwards compatibility. No migration guides, no compatibility shims, no support for old configurations or data. @@ -39,26 +38,27 @@ When connected to a **CleverAgents server** (developed independently), the clien While CleverAgents leverages LangGraph and LangChain for the underlying LLM runtime primitives (tool calling, graphs, routing), its value lies in what it builds on top: -- **CleverAgents** provides: - - A **first-class plan lifecycle** (Action/Strategize/Execute/Apply) for breaking down and tracking complex work, - - A **project + resource model** for grounding tasks in real codebases, databases, documents, and infrastructure, - - A consistent **actor abstraction** for defining and composing intelligent agents, - - A consistent **skill abstraction** for anything an agent can execute, - - A **sandbox + checkpoint** safety model for safe, reversible execution, - - A **CLI/TUI/Web UX** for controlling and monitoring large multi-step autonomous work. +* **CleverAgents** provides: + + * A **first-class plan lifecycle** (Action/Strategize/Execute/Apply) for breaking down and tracking complex work, + * A **project + resource model** for grounding tasks in real codebases, databases, documents, and infrastructure, + * A consistent **actor abstraction** for defining and composing intelligent agents, + * A consistent **skill abstraction** for anything an agent can execute, + * A **sandbox + checkpoint** safety model for safe, reversible execution, + * A **CLI/TUI/Web UX** for controlling and monitoring large multi-step autonomous work. ### Key Concepts -| Concept | Definition | -| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| **Plan** | A tracked lifecycle for a single unit-of-work (which may spawn subplans). Phases: Action -> Strategize -> Execute -> Apply | -| **Action** | A reusable plan template. Created via CLI commands (NOT YAML files). | -| **Actor** | Anything conversational; may be a single agent/LLM or an entire graph. Defined via YAML configuration files. Always named `/`. | -| **Project** | A collection of resources + configuration. Created via CLI commands (NOT YAML files). | -| **Resource** | Anything that can be read/written/queried. Each resource defines its own sandbox strategy. | -| **Skill** | A callable capability defined inline in actor YAML as tool nodes. | -| **Namespace** | Scoping mechanism: `local/`, `/`, `/`, or provider namespaces (`openai/`, `anthropic/`). | -| **Decision** | A recorded choice point made during Strategize that affects downstream work. Forms a tree enabling correction and replay. | +| Concept | Definition | +|---------|------------| +| **Plan** | A tracked lifecycle for a single unit-of-work (which may spawn subplans). Phases: Action -> Strategize -> Execute -> Apply | +| **Action** | A reusable plan template. Created via CLI commands (NOT YAML files). | +| **Actor** | Anything conversational; may be a single agent/LLM or an entire graph. Defined via YAML configuration files. Always named `/`. | +| **Project** | A collection of resources + configuration. Created via CLI commands (NOT YAML files). | +| **Resource** | Anything that can be read/written/queried. Each resource defines its own sandbox strategy. | +| **Skill** | A callable capability defined inline in actor YAML as tool nodes. | +| **Namespace** | Scoping mechanism: `local/`, `/`, `/`, or provider namespaces (`openai/`, `anthropic/`). | +| **Decision** | A recorded choice point made during Strategize that affects downstream work. Forms a tree enabling correction and replay. | --- @@ -74,21 +74,18 @@ While CleverAgents leverages LangGraph and LangChain for the underlying LLM runt ### Core Architectural Requirements **Scalability**: The system must handle massive codebases (50,000+ files) through: - - Three-tier memory architecture (hot/warm/cold) - Hierarchical task decomposition - Bounded dependency closures - Lazy resource sandboxing **Reliability**: Prevent cascading failures through: - - Complete execution isolation via sandboxes - Multi-layer semantic error prevention - Checkpoint-based rollback capabilities - Invariant enforcement throughout execution **Autonomy with Control**: Progressive automation through: - - Three-level automation system (manual, review-before-apply, full) - Decision correction without full re-execution - Confidence-based escalation @@ -97,7 +94,6 @@ While CleverAgents leverages LangGraph and LangChain for the underlying LLM runt --- ## Continuous Testing and Documentation Policy - - Do not mark any parent checklist item complete until **all** subordinate Code, Document, Tests tasks and any generated `Fix – …` tasks are resolved and the associated Notes section has the latest context. - Every time new information appears, extend the corresponding Notes section immediately with explicit references to code locations and decisions. - Maintain a running catalog of Behave commands, Robot suites, fixtures, and environments in the Notes sections to assist subsequent contributors. @@ -106,7 +102,6 @@ While CleverAgents leverages LangGraph and LangChain for the underlying LLM runt --- ## Completion Criteria - The implementation concludes only when every checklist item and spawned remediation task is checked, all Notes sections contain final decisions and references, and the full Behave and Robot test suites (unit, integration, end-to-end, benchmarking, packaging, documentation) pass without outstanding failures. --- @@ -120,11 +115,11 @@ Action -> Strategize -> Execute -> Apply -> Applied (terminal) ``` | Current Phase | Command Verb | Next Phase | -| ------------- | ------------ | ---------- | -| (none) | `create` | Action | -| Action | `use` | Strategize | -| Strategize | `execute` | Execute | -| Execute | `apply` | Applied | +|---------------|--------------|------------| +| (none) | `create` | Action | +| Action | `use` | Strategize | +| Strategize | `execute` | Execute | +| Execute | `apply` | Applied | ### Plan States (Per Phase) @@ -135,28 +130,24 @@ Action -> Strategize -> Execute -> Apply -> Applied (terminal) ### Key Architectural Components **Multi-tier Memory System**: - - **Hot tier**: Immediate working context in LLM context window - **Warm tier**: Recent decisions and contexts from current plan tree - **Cold tier**: Historical decisions from past plans, queryable but not in active memory - Context snapshots with cryptographic hashes preserve complete decision context **Dependency Closure Computation**: - - Resource-aware analysis during Strategize - Hierarchical scoping with explicit resource lists - Lazy expansion prevents closure explosion - Interface-based boundaries for modular changes **Execution Coordination**: - - Complete isolation via per-plan sandboxes - Resource-specific sandbox strategies (git worktrees, transactions, etc.) - Hierarchical merge resolution - Checkpoint-based coordination for rollback **Semantic Error Prevention**: - - Decision-time validation during Strategize - Execution-time semantic guards in actors - Invariant enforcement throughout @@ -164,12 +155,12 @@ Action -> Strategize -> Execute -> Apply -> Applied (terminal) ### Namespace Rules -| Namespace | Scope | Storage | -| ----------------------------- | ------------------------- | --------------- | -| `local/` | Current machine only | Local database | -| `/` | Personal server namespace | Server database | -| `/` | Organization namespace | Server database | -| `openai/`, `anthropic/`, etc. | Built-in LLM actors | N/A (built-in) | +| Namespace | Scope | Storage | +|-----------|-------|---------| +| `local/` | Current machine only | Local database | +| `/` | Personal server namespace | Server database | +| `/` | Organization namespace | Server database | +| `openai/`, `anthropic/`, etc. | Built-in LLM actors | N/A (built-in) | ### Configuration Philosophy @@ -186,14 +177,14 @@ All environment variables needed during testing are stored in the `.env` file in ### Current Environment Variables -| Variable Name | Service | Usage | -| -------------------- | ------------- | ----------------------------------------------------- | -| `OPENROUTER_API_KEY` | OpenRouter | Access to multiple LLM models through OpenRouter API | -| `OPENAI_API_KEY` | OpenAI | Direct access to OpenAI models (GPT-3.5, GPT-4, etc.) | -| `ANTHROPIC_API_KEY` | Anthropic | Access to Claude models | -| `GOOGLE_API_KEY` | Google AI | Access to Google's API for web searches | -| `GEMINI_API_KEY` | Google Gemini | Access to Google's Gemini models | -| `HF_TOKEN` | Hugging Face | Access to Hugging Face models and datasets | +| Variable Name | Service | Usage | +|--------------|---------|-------| +| `OPENROUTER_API_KEY` | OpenRouter | Access to multiple LLM models through OpenRouter API | +| `OPENAI_API_KEY` | OpenAI | Direct access to OpenAI models (GPT-3.5, GPT-4, etc.) | +| `ANTHROPIC_API_KEY` | Anthropic | Access to Claude models | +| `GOOGLE_API_KEY` | Google AI | Access to Google's API for web searches | +| `GEMINI_API_KEY` | Google Gemini | Access to Google's Gemini models | +| `HF_TOKEN` | Hugging Face | Access to Hugging Face models and datasets | --- @@ -246,20 +237,18 @@ All 10 ADRs have been created and package structure established. See the Phase 1 The following work from the previous implementation has been completed and will be preserved/adapted: #### Completed Infrastructure - -- [x] LangChain/LangGraph dependencies and integration (ADR-011) -- [x] PlanGenerationGraph, ContextAnalysisAgent, AutoDebugGraph workflows -- [x] Memory service with EntityMemory -- [x] SQLite persistence with Alembic migrations -- [x] CLI streaming integration -- [x] Provider adapters (OpenAI, Anthropic, Google, OpenRouter) -- [x] Actor configuration system (Stage 7.5) -- [x] Test coverage at 95% +- [X] LangChain/LangGraph dependencies and integration (ADR-011) +- [X] PlanGenerationGraph, ContextAnalysisAgent, AutoDebugGraph workflows +- [X] Memory service with EntityMemory +- [X] SQLite persistence with Alembic migrations +- [X] CLI streaming integration +- [X] Provider adapters (OpenAI, Anthropic, Google, OpenRouter) +- [X] Actor configuration system (Stage 7.5) +- [X] Test coverage at 95% #### Phase 2 Notes (Preserved from Previous Work) **2025-11-22**: Week 12 Complete, Phase 2 Core Functionality DONE - - CLI Streaming Integration fully implemented - AutoDebugGraph Implementation complete - Mock provider enhancements with configurable failure modes @@ -271,229 +260,64 @@ The following work from the previous implementation has been completed and will **2025-12-17**: Stage 7 performance optimization complete **2026-02-02**: Stage 7.5 Actor Configuration System complete **2026-02-05**: Stage A1 & A2 Complete - Plan and Action Domain Models - - Created `src/cleveragents/domain/models/core/plan.py` with: - - `PlanPhase` enum (ACTION, STRATEGIZE, EXECUTE, APPLY, APPLIED) - - `ActionState` enum (AVAILABLE, DRAFT, ARCHIVED) - - `ProcessingState` enum (QUEUED, PROCESSING, ERRORED, COMPLETE, CANCELLED) - - `NamespacedName` model with parse() and str() methods - - `PlanIdentity` model with ULID validation - - `Plan` model with full lifecycle support - - `can_transition()` function for phase transition validation + - `PlanPhase` enum (ACTION, STRATEGIZE, EXECUTE, APPLY, APPLIED) + - `ActionState` enum (AVAILABLE, DRAFT, ARCHIVED) + - `ProcessingState` enum (QUEUED, PROCESSING, ERRORED, COMPLETE, CANCELLED) + - `NamespacedName` model with parse() and str() methods + - `PlanIdentity` model with ULID validation + - `Plan` model with full lifecycle support + - `can_transition()` function for phase transition validation - Created `src/cleveragents/domain/models/core/action.py` with: - - `ActionArgument` model with parse() method for CLI argument parsing - - `Action` model with strategy/execution actor references - - Argument validation including type checking + - `ActionArgument` model with parse() method for CLI argument parsing + - `Action` model with strategy/execution actor references + - Argument validation including type checking - Added 52 Behave test scenarios across 2 feature files: - - `features/plan_model.feature` (30 scenarios) - - `features/action_model.feature` (22 scenarios) + - `features/plan_model.feature` (30 scenarios) + - `features/action_model.feature` (22 scenarios) - All new tests pass, existing tests unaffected **2026-02-05**: Stage A3 Complete - PlanLifecycleService - - Created `src/cleveragents/application/services/plan_lifecycle_service.py` with: - - Full plan lifecycle management (Action -> Strategize -> Execute -> Apply -> Applied) - - Action CRUD operations (create, get, list, make_available, archive) - - Plan creation via `use_action()` which transitions Action to Strategize - - Phase transition methods: `execute_plan()`, `apply_plan()` - - State management: `start_*()`, `complete_*()`, `fail_*()` for each phase - - `cancel_plan()` for non-terminal plans - - Custom exceptions: `InvalidPhaseTransitionError`, `ActionNotAvailableError`, `PlanNotReadyError` - - In-memory storage (to be replaced with persistence in Stage A5) + - Full plan lifecycle management (Action -> Strategize -> Execute -> Apply -> Applied) + - Action CRUD operations (create, get, list, make_available, archive) + - Plan creation via `use_action()` which transitions Action to Strategize + - Phase transition methods: `execute_plan()`, `apply_plan()` + - State management: `start_*()`, `complete_*()`, `fail_*()` for each phase + - `cancel_plan()` for non-terminal plans + - Custom exceptions: `InvalidPhaseTransitionError`, `ActionNotAvailableError`, `PlanNotReadyError` + - In-memory storage (to be replaced with persistence in Stage A5) - Added python-ulid dependency for ULID generation - Added 29 Behave test scenarios in `features/plan_lifecycle_service.feature` - Total new test scenarios: 81 (30 + 22 + 29) **2026-02-05**: Stage A4 In Progress - Plan CLI Commands - - Created `src/cleveragents/cli/commands/action.py` with: - - `agents [--data-dir PATH] [--config-path PATH] action create` - Create new action with strategy/execution actors, definition of done, arguments - - `agents [--data-dir PATH] [--config-path PATH] action list` - List actions with filtering by namespace, state - - `agents [--data-dir PATH] [--config-path PATH] action show` - Show action details by ID or name - - `agents [--data-dir PATH] [--config-path PATH] action available` - Make draft action available for use - - `agents [--data-dir PATH] [--config-path PATH] action archive` - Archive an action (soft delete) + - `agents [--data-dir PATH] [--config-path PATH] action create` - Create new action with strategy/execution actors, definition of done, arguments + - `agents [--data-dir PATH] [--config-path PATH] action list` - List actions with filtering by namespace, state + - `agents [--data-dir PATH] [--config-path PATH] action show` - Show action details by ID or name + - `agents [--data-dir PATH] [--config-path PATH] action available` - Make draft action available for use + - `agents [--data-dir PATH] [--config-path PATH] action archive` - Archive an action (soft delete) - Extended `src/cleveragents/cli/commands/plan.py` with v3 lifecycle commands: - - `agents [--data-dir PATH] [--config-path PATH] plan use --project ` - Use action to create plan in Strategize phase + - `agents [--data-dir PATH] [--config-path PATH] plan use ` - Use action to create plan in Strategize phase (legacy `--project` retained in old notes) - `agents [--data-dir PATH] [--config-path PATH] plan execute [plan_id]` - Transition plan from Strategize to Execute - `agents [--data-dir PATH] [--config-path PATH] plan apply [plan_id]` - Transition plan from Execute to Apply - `agents [--data-dir PATH] [--config-path PATH] plan status [plan_id]` - Show v3 plan status and details - - `agents [--data-dir PATH] [--config-path PATH] plan list` - List v3 lifecycle plans with filtering + - `agents [--data-dir PATH] [--config-path PATH] plan list [--phase ] [--state ] [--project ] [--action ]` - List v3 lifecycle plans with filtering - `agents [--data-dir PATH] [--config-path PATH] plan cancel ` - Cancel a non-terminal plan - Registered action commands in CLI main.py - Added 15 Behave test scenarios in `features/action_cli.feature` - Total test scenarios: 96 (81 + 15) -**2026-02-09**: Task Q0.6b Complete - README.md Setup Instructions [Brent] - -- Updated README.md Quick Start: added `dev` extras to `pip install`, added `scripts/setup-dev.sh` step -- Updated README.md Developing section: fixed `oxt` typo -> `nox`, added quality/security nox sessions (security_scan, dead_code, complexity, pre_commit, adr_compliance), added note about pre-commit hooks, linked to `docs/development/quality-automation.md` - -**2026-02-09**: Ruff Cleanup in src/cleveragents/ - StrEnum Migration [Brent] - -- Fixed all 26 ruff findings in `src/cleveragents/` (all UP042: `str, Enum` -> `StrEnum`) + 12 consequent F401 unused `Enum` imports -- Migrated 26 enum classes across 15 files from `class Foo(str, Enum)` to `class Foo(StrEnum)` -- `StrEnum` (Python 3.11+) is the modern replacement; project targets Python 3.13 -- Semantic difference: `str(StrEnum.MEMBER)` returns the value (e.g., `"foo"`) rather than `"ClassName.MEMBER"` — this is the correct/intended behavior for config/JSON string enums -- Verified: no code uses `str()` on enum members in the old format; all tests pass (304 scenarios, 0 failures) -- Files: 15 domain model files + `memory_service.py` + `providers/registry.py` - -**2026-02-09**: Task Q0.9 Complete - Ruff Lint Findings in features/ [Brent] - -- Fixed all 200 ruff lint findings in `features/` directory -> **0 findings** -- **Config-level suppressions** (168 findings): - - Added `per-file-ignores` in `pyproject.toml` for Behave-specific patterns: - - `features/steps/*.py`: F811 (65 redefined `step_impl` — Behave idiom), E501 (long step decorator strings) - - `features/mocks/*.py`, `features/environment.py`: E501 -- **Manual fixes** (31 findings across 18 files): - - 11x SIM115: `NamedTemporaryFile` refactored to use `with` context manager (`actor_cli_steps.py`, `actor_cli_run_steps.py`) - - 4x UP028: `for/yield` -> `yield from` (google, openai, openrouter, langchain provider steps) - - 3x SIM117: Nested `with` -> single `with` with parenthesized contexts (`plan_full_coverage_steps.py`, `plan_service_steps.py`) - - 3x RUF005: `list + [item]` -> `[*list, item]` unpacking - - 2x B904: Added `from exc` to `raise` inside `except` (enums, retry patterns) - - 2x RUF012: Added `ClassVar` annotations (`vector_store_service_steps.py`) - - 2x SIM105: `try/except/pass` -> `contextlib.suppress(Exception)` - - 1x each: B007 (unused loop var), B018 (noqa suppression), F821 (missing `Any` import), SIM102 (collapsible if), I001 (auto-fixed unsorted import) -- **Verification**: All affected behave tests pass (155 scenarios, 0 failures) -- **Files modified**: `pyproject.toml` (config), `environment.py`, and 17 step files in `features/steps/` - -**2026-02-09**: Task Q0.8 Complete - Bandit Security Findings Remediation [Brent] - -- Fixed all 16 pre-existing bandit findings (2 HIGH, 3 MEDIUM, 11 LOW) -> **0 findings** -- **Security hardening** (HIGH+MEDIUM): - - Replaced `jinja2.Environment` with `jinja2.sandbox.SandboxedEnvironment` in `yaml_template_engine.py` and `stream_router.py` — prevents template injection - - Added `_validate_code_ast()` helper: AST-based pre-validation for `exec()` in `SimpleToolAgent` — rejects imports, `exec()`/`eval()`/`compile()`/`__import__()`/`getattr()`/`setattr()` calls, global/nonlocal statements - - Added `_validate_lambda_ast()` helper: restricts transform `eval()` to lambda-only expressions via AST parsing - - Suppressed `0.0.0.0` bind default (`# nosec B104`) — intentional, configurable via `CLEVERAGENTS_SERVER_HOST` -- **Code quality** (LOW): - - Replaced 6 `assert` statements with proper `if`/`raise` (TypeError, RuntimeError, typer.BadParameter) — asserts stripped in optimized bytecode - - Replaced `try/except/pass` with `contextlib.suppress(Exception)` (2 locations in dispose()) - - Added logging to previously-silent exception handlers (migration_runner, nodes retry loop) - - Suppressed false positive `"token_count": 0` flagged as hardcoded password (`# nosec B105`) -- **Files modified**: `stream_router.py`, `yaml_template_engine.py`, `settings.py`, `context_service.py`, `memory_service.py`, `retry_patterns.py`, `context.py` (CLI), `plan_service.py`, `migration_runner.py`, `nodes.py` -- **Verification**: `bandit -r src/ -c pyproject.toml` → 0 findings; targeted behave tests pass; smoke tests for AST validation pass - -**2026-02-09**: Stages Q0, Q1, Q2 Complete - Full Quality Automation Setup [Brent] - -**Stage Q0 - Pre-commit Hooks:** - -- Created `.pre-commit-config.yaml` with 12 hooks across 5 categories: - - Branch protection: `no-commit-to-branch` (prevents commits to main) - - General checks: `check-yaml`, `check-toml`, `check-json`, `check-merge-conflict`, `check-added-large-files`, `end-of-file-fixer`, `trailing-whitespace`, `debug-statements` - - Ruff: `ruff-format` (auto-fix), `ruff` (lint with safe auto-fix) - - Pyright: local system hook running type checking on `src/` only - - Bandit: security scanning with `pyproject.toml` configuration on `src/` only - - Vulture: dead code detection with whitelist at `vulture_whitelist.py` - - Semgrep: custom rules in `.semgrep.yml` for eval/exec/os.system/pickle detection (graceful skip when not installed) - - Commitizen: conventional commit message validation at commit-msg stage -- Added dev dependencies to `pyproject.toml`: `pre-commit>=3.6.0`, `bandit[toml]>=1.7.5`, `vulture>=2.10`, `radon>=6.0.1` -- Added `[tool.bandit]` and `[tool.vulture]` sections to `pyproject.toml` -- Created `vulture_whitelist.py` for false positive suppression (exc_tb, build_data) -- Created `.semgrep.yml` with 5 custom security rules -- Created `scripts/setup-dev.sh` for developer environment setup -- Added 4 new nox sessions: `pre_commit`, `security_scan`, `dead_code`, `complexity` -- **Discovery**: CI platform is Forgejo (`.forgejo/`), NOT GitHub. Stage Q1 must use Forgejo Actions, not GitHub Actions. -- **Discovery**: Pre-existing security findings in production code need remediation (see Q0.8 spawned task) -- **Discovery**: 37 source files have formatting issues, ~27 files have trailing whitespace - pre-existing debt -- **Discovery**: `features/steps/actor_cli_steps.py` has many F811 (redefined step_impl) violations - Behave pattern -- **Discovery**: Average code complexity is A (3.56) across 979 blocks - good baseline -- **Discovery**: High complexity methods identified: `LegacyDataMigrator.migrate_project_data` E(37), `ProviderRegistry._create_provider_llm` C(20), `ProviderRegistry.create_ai_provider` C(18) -- Key files: `.pre-commit-config.yaml`, `.semgrep.yml`, `vulture_whitelist.py`, `scripts/setup-dev.sh` - -**Stage Q1 - CI/CD Pipeline:** - -- Extended `.forgejo/workflows/ci.yml` with 3 new jobs: - - `security`: bandit scan (JSON report + high-severity gate) + vulture dead code detection - - `quality`: radon complexity check (grade F fails build) + JSON report - - `coverage`: behave tests with coverage measurement, fail-under=85%, XML artifact -- Updated `docker` and `helm` jobs to depend on `security` (fail-fast on security issues) -- Created `scripts/check-quality-gates.py` aggregating: coverage, typecheck, security, dead code, complexity -- All reports uploaded as artifacts for downstream consumption - -**Stage Q2 - Advanced Automation:** - -- Created `.forgejo/workflows/nightly-quality.yml` for nightly quality monitoring: - - Runs at midnight UTC (cron: "0 0 \* \* \*") + manual trigger support - - Full lint, typecheck, security scan (all severities), dead code, complexity analysis - - Behave tests with coverage measurement - - Quality trend JSON generation with timestamp + metrics - - 90-day artifact retention for trend analysis -- Created `scripts/check-adr-compliance.py` with AST-based checks for: - - ADR-002: No threading imports in application layer - - ADR-003: Services use constructor dependency injection - - ADR-007: No direct SQLAlchemy usage in service layer -- Added `nox -s adr_compliance` session -- Created `.forgejo/pull_request_template.md` with quality checklist -- Created `docs/development/quality-automation.md` with full documentation: - - Quick start, pre-commit hooks reference, CI jobs table, security scanning guide - - Complexity monitoring grades, quality gates, troubleshooting - -**New nox sessions added:** `pre_commit`, `security_scan`, `dead_code`, `complexity`, `adr_compliance` -**Total files created:** 9 new files -**Total files modified:** 3 files (pyproject.toml, noxfile.py, .forgejo/workflows/ci.yml) - -**2026-02-10**: Task 10B.4 - Quality Metrics Baseline Established [Brent] - -- Ran full quality suite via nox to establish current baseline: - - **Unit Tests**: 105 features, 1613 scenarios, 7555 steps - ALL PASS - - **Lint (ruff)**: 0 findings - - **Typecheck (pyright)**: 0 errors, 0 warnings - - **Security (bandit)**: 0 findings (0 HIGH, 0 MEDIUM, 0 LOW) - - **Dead Code (vulture)**: 0 findings - - **Complexity (radon)**: Average A (3.56), 981 blocks analyzed, no grade-F methods - - High complexity methods to monitor: `LegacyDataMigrator.migrate_project_data` E(37), `Action.validate_arguments` C(20), `ProviderRegistry._create_provider_llm` C(20), `ProviderRegistry.create_ai_provider` C(18), `Settings.resolve_provider_defaults` C(18) - - **Coverage**: 96% (9860 statements, 269 missing, 2852 branches, 213 branch-miss) -- Fixed pre-existing test failure: `plan_lifecycle_cli_coverage.feature` scenario "Plan lifecycle list shows project summaries" - Rich table column wrapping at narrow terminal widths caused `+1 more` text to be split across rows. Fixed by patching console width to 200 in test setup. -- Fixed missing dependency: added `langchain-anthropic>=0.2.0` to `pyproject.toml` (was imported in `src/cleveragents/providers/llm/anthropic_provider.py` but not declared) - -**2026-02-10**: Task Q1.5 Complete - Branch Protection Rules Documentation [Brent] - -- Created `docs/development/ci-cd.md` (224 lines) documenting: - - Branch protection rules for `master` (required status checks, review requirements, push/force-push/deletion blocks) - - Step-by-step Forgejo branch protection setup instructions - - Review priority matrix (P0: architecture/security, P1: algorithms, P2: features, P3: tests/docs) - - CI job dependency graph and quality gates summary table - - Nightly quality monitoring reference - - Local development workflow quick-reference -- Cross-references existing `docs/development/quality-automation.md` and `.forgejo/pull_request_template.md` -- Required CI checks documented: `lint`, `typecheck`, `security`, `quality`, `behave`, `coverage`, `build` -- Review requirement: 1 approving review, selective depth by priority matrix - -**2026-02-10**: Task 10C.1 Complete - Edge Case Test Scenarios [Brent] - -- Created `features/edge_case_plan_scenarios.feature` (26 scenarios, 141 steps) covering: - - **Concurrent plan execution** (6 scenarios): Duplicate strategize/execute/apply start attempts, concurrent complete+fail on same phase, two plans from same action, concurrent transitions on different plans - - **Resource conflict scenarios** (7 scenarios): Read-only file MODIFY failure, overlapping CREATE+MODIFY on same path, CREATE with None content, MOVE with missing source, DELETE of already-deleted file, file paths with spaces, deeply nested directory creation - - **Validation failure chains** (6 scenarios): Multiple simultaneous argument validation failures (missing + wrong type), unknown + missing arguments combined, empty plan description rejection, ACTION phase with processing state, invalid namespace characters, invalid name characters - - **Rollback edge cases** (7 scenarios): Partial apply failure (first change persists on disk, second unchanged), errored plan cannot restart, fail preserves error message, errored plan not terminal, cancel preserves phase, errored strategize rejects execute, failed strategize rejects complete -- Created `features/steps/edge_case_plan_steps.py` with step definitions for all 26 scenarios -- Verified no step name collisions with existing 104 feature files -- All quality gates pass: lint 0 findings, typecheck 0 errors, full suite 106 features / 1639 scenarios / 7696 steps ALL PASS - -**2026-02-10**: Task 10C.4 Complete - Validation Test Fixtures [Brent] - -- Created `features/validation_test_fixtures.feature` (34 scenarios, 81 steps) covering 6 validation domains: - - **AST security validation** (`_validate_code_ast`): 12 scenarios testing import/global/nonlocal/exec/eval/compile/**import**/getattr/setattr rejection, syntax errors, and safe code acceptance - - **Lambda AST validation** (`_validate_lambda_ast`): 4 scenarios testing valid lambda, non-lambda rejection, syntax errors, function call rejection - - **Python content sanitization** (`_sanitize_python_content`): 4 scenarios testing passthrough, code fence stripping, docstring wrapping, irrecoverable syntax (null byte) - - **Project model validation**: 6 scenarios testing invalid chars, slashes, exclamation, valid names, relative path resolution, empty name - - **Change list coercion** (`_coerce_change_list`): 4 scenarios testing empty list, mixed entries, non-list, non-change entry - - **ActionArgument parsing**: 4 scenarios testing too few parts, invalid type, invalid requirement, reserved keyword name -- Created `features/steps/validation_test_fixture_steps.py` with complete step definitions for all 34 scenarios -- Fixed step name collisions: renamed `I create a project with name` -> `I create a project fixture with name` and `the project path should be absolute` -> `the project fixture path should be absolute` to avoid conflicts with `database_integration_steps.py` and `domain_models_steps.py` -- Moved inline import (`ArgumentRequirement`, `ArgumentType`) to file-level per CONTRIBUTING.md rules -- Fixed irrecoverable syntax scenario: `"def broken("` is actually recoverable via docstring wrapping; replaced with null byte input which is truly irrecoverable -- All quality gates pass: lint 0 findings, typecheck 0 errors, full suite 107 features / 1673 scenarios / 7777 steps ALL PASS - **2026-02-06**: CRITICAL ARCHITECTURAL DECISION - Tool-Based Resource Modification - - **REPLACED**: OutputParser/code fence parsing approach - **WITH**: Tool-based change tracking (modern approach used by Claude Code, Cursor, Aider) - **Key changes**: - LLMs call skills/tools directly (edit_file, write_file, delete_file, etc.) - Skills operate on sandbox state directly - ChangeSet is built from skill invocation history, NOT by parsing LLM text output - - Added built-in resource skills (C3.6): file ops, dir ops, search, git ops - - Added MCP skill adapter (C3.7): connect to external MCP servers +- Added built-in resource skills (now C4.file/C4.search/C4.git): file ops, dir ops, search, git ops +- Added MCP skill adapter (now C7.mcp): connect to external MCP servers - Replaced C4 "Multi-File ChangeSet Generation" with "Tool-Based Change Tracking" - Added SkillInvocationTracker and ToolCallRouter components - **Rationale**: @@ -503,9 +327,9 @@ The following work from the previous implementation has been completed and will - Resource-agnostic (works for files, databases, APIs, any resource type) - Compatible with MCP standard for external tools - See `docs/specification.md` sections: - - "Tool-Based Resource Modification (Modern Architecture)" - - "Unified Resource Abstraction Layer" - - "MCP Integration Architecture" + - "Tool-Based Resource Modification (Modern Architecture)" + - "Unified Resource Abstraction Layer" + - "MCP Integration Architecture" --- @@ -513,22 +337,21 @@ The following work from the previous implementation has been completed and will ### Milestone Overview -| Milestone | Target Date | Description | -| -------------------------------- | --------------- | ------------------------------------------------------------------------------------------ | -| **M0: Foundation** | Day 0 (Current) | Existing LangGraph infrastructure preserved | -| **M1: Minimal Plan Lifecycle** | +7 days | Basic Action -> Strategize -> Execute -> Apply working for source code | -| **M2: Projects & Resources** | +10 days | Project/Resource CLI commands, local filesystem sandbox | -| **M3: Actors & Skills** | +14 days | YAML actor loading, skill execution, multi-file generation | -| **M4: Decision Tree** | +21 days | Decision recording during Strategize, basic correction | -| **M5: Multi-Project & Subplans** | +25 days | Subplan spawning, parallel execution | -| **M6: Large Project Autonomy** | +30 days | Handle 10K+ file projects, decision correction, deep subplan hierarchies (LOCAL MODE ONLY) | -| **M7: Server Connectivity** | +35+ days | Client-server communication for remote project support (server developed independently) | -| **M8: Full Feature Set** | +40 days | All spec features complete | +| Milestone | Target Date | Description | +|-----------|-------------|-------------| +| **M0: Foundation** | Day 0 (Current) | Existing LangGraph infrastructure preserved | +| **M1: Minimal Plan Lifecycle** | +7 days | Basic Action -> Strategize -> Execute -> Apply working for source code | +| **M2: Projects & Resources** | +10 days | Project/Resource CLI commands, local filesystem sandbox | +| **M3: Actors & Skills** | +14 days | YAML actor loading, skill execution, multi-file generation | +| **M4: Decision Tree** | +21 days | Decision recording during Strategize, basic correction | +| **M5: Multi-Project & Subplans** | +25 days | Subplan spawning, parallel execution | +| **M6: Large Project Autonomy** | +30 days | Handle 10K+ file projects, decision correction, deep subplan hierarchies (LOCAL MODE ONLY) | +| **M7: Server Connectivity** | +35+ days | Client-server communication for remote project support (server developed independently) | +| **M8: Full Feature Set** | +40 days | All spec features complete | ### Critical Path to 7-Day MVP (Source Code Only) **WEEK 1 GOAL**: A minimally usable application that can: - 1. Create an action from CLI 2. Use the action on a source code project 3. Execute with sandbox isolation @@ -606,7 +429,6 @@ MERGE POINT 3: After Day 30 (M6 - Large Project Autonomy) ## Quick Reference for Development ### Tool Commands - ```bash # Development setup pip install -e .[dev,tests,docs] # Install with all extras @@ -629,7 +451,6 @@ nox -s docs # Build documentation ``` ### Key Files and Their Purpose - - `pyproject.toml` - All project configuration (no setup.py, no requirements.txt) - `noxfile.py` - All task automation (no Makefile, no scripts/) - `features/` - Behave unit tests (no tests/ directory) @@ -637,8 +458,7 @@ nox -s docs # Build documentation - `docs/reference/` - Discovery artifacts from Phase 0 - `docs/architecture/decisions/` - ADRs from Phase 1 -### Environment Variables (CLEVERAGENTS\_\* only) - +### Environment Variables (CLEVERAGENTS_* only) ```bash # Core configuration CLEVERAGENTS_HOME=~/.cleveragents @@ -652,6 +472,129 @@ CLEVERAGENTS_TEST_MODE=true --- +## Schedule Adhereance + +### 2026-02-12 (Day 2 since kickoff on 2026-02-11) +- Milestone calendar (relative): Day 7/M1 = 2026-02-18, Day 10/M2 = 2026-02-21, Day 14/M3 = 2026-02-25, Day 21/M4 = 2026-03-04, Day 25/M5 = 2026-03-08, Day 30/M6 = 2026-03-13, Day 35/M7 = 2026-03-18. +- Current baseline vs spec: action model still states CLI-only (not YAML), PlanLifecycleService is in-memory, `plan` CLI still contains legacy tell/build/apply paths, and legacy DB models (`projects`, `plans`, `changes`) remain; these are blockers for M1 persistence + CLI alignment. +- Schedule variance: A2b/A4b/A5 + B1/B2/C0 are still open on Day 2, leaving ~5 days to M1; we are ~2-3 days behind the critical path unless persistence + resources + tool registry start in parallel today. +- Variance snapshot (Day 2): Week 1 now explicitly allocates Rui for test scaffolds alongside Jeff/Luis/Hamza; this increases parallel test throughput but does not change the critical path for A2b/A4b/A5/B1. +- Sequencing confirmation (Day 2-6): A2b.alpha + A2b.beta -> A4b.alpha -> A4b.beta -> A5.alpha -> A5.beta/A5.gamma -> A5.legacy; B1.core + C0.domain run in parallel; B1/C0 DB migrations must rebase after A5.alpha head; A4b.alpha can scaffold in parallel but only merges after A2b.alpha/beta/gamma. +- Day 2-6 allocation (compressed): Day 2 Jeff starts A2b.alpha + A5.alpha + A4b.alpha scaffolding; Hamza starts B1.core + built-in types; Luis starts A5.beta; Aditya starts C1.schema/examples; Rui starts A2b/A4b/A5/B1 test scaffolds; Brent lands Q0-Minimum. Day 3-4: Jeff finishes A2b.alpha + A4b.alpha and advances A5.alpha; Hamza finishes B1.core and starts B2.persistence; Luis completes A5.beta and starts A5.gamma; Aditya continues C1; Rui grows suites. Day 5-6: Jeff closes A5.alpha + A4b.alpha polish, prepares A5.legacy; Luis completes A5.gamma DI; Hamza advances B2.service; Rui aligns A4b.beta tests and runs nox. +- Staffing assumptions confirmed (Day 8-14): Jeff leads C9 execute/apply, Hamza owns D1/D2 decisions, Luis owns E1 subplans, Aditya owns E2.actor, Rui handles test scaffolds, Brent runs QA gates. +- Risk: Alembic head contention between A5.alpha/B1/C0 increases rebase churn; mitigation is to rebase before merge and keep a single linear Alembic head. +- Risk: A4b CLI alignment may drift from A2b domain updates; mitigation is to lock CLI outputs and keep A4b.beta tests tied to exact fields. +- Risk: Coverage/nox gates can stall merges as suites grow; mitigation is daily nox runs and early flaky-test isolation. +- Risk: Resource/tool registry schema drift can block project/skill wiring; mitigation is to finalize YAML schemas before persistence wiring. +- Risk: Large-project performance (M6) may slip if context indexing or decomposition is slow; mitigation is to run ASV benchmarks by Day 22 and enforce file/token thresholds. +- Micro-schedule (Days 1-40, block-level): + +**Days 1-7** +| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Day 1 AM | A5.alpha migration draft | A5.beta ORM plan | B1.core models prep | C1 schema prep | A5 test scaffolds | Q0-Minimum hooks | A5.alpha draft | +| Day 1 PM | A5.alpha finalize | A5.beta ORM models | B1.core models | C1 schema start | A5 migration tests | Q0-Minimum CI | A5.alpha commit 1 | +| Day 2 AM | A2b.alpha core fields + A5.alpha-1 draft | A5.beta mapping plan | B1.core resource_type model | C1.schema start | A2b/A4b test scaffolds | Q0-Minimum hooks | A2b.alpha draft ready | +| Day 2 PM | A4b.alpha scaffolding + A5.alpha-1 finalize | A5.beta ORM models | B1.core built-in types | C1.examples start | A5 migration tests | Q0-Minimum CI | B1.core commit ready | +| Day 3 AM | A5.alpha-2/3 migrations | A5.gamma repo | B2.persistence draft | C1.schema finalize | A4b tests | Q0-Minimum coverage | A5.alpha commits 1-2 | +| Day 3 PM | A2b.alpha finalize + A4b.alpha main | A5.gamma service | B2.persistence finalize | C1.examples finalize | Robot smoke scaffolds | nox gates | A2b.alpha commit | +| Day 4 AM | A4b.alpha finalize | A5.gamma DI wiring | B2.service start | C2.loader prep | A4b.beta tests | Q0-Minimum signoff | A4b.alpha commit | +| Day 4 PM | A5.alpha-4 finalize | A5.gamma tests | B2.service | C2.loader start | A5 tests | nox gates | A5.alpha complete | +| Day 5 AM | A5.legacy prep + C0.domain start | A5.gamma finalize | B3.cli prep | C2.loader | A4b.beta finalize | QA review | A5.gamma commit | +| Day 5 PM | A5.legacy commit | A5.gamma DI polish | B3.cli start | C2.loader | Run nox | QA review | A5.legacy ready | +| Day 6 AM | C0.domain finalize | A5.gamma DI merge | B3.cli | C2.loader | A4b.beta robot | QA signoff | C0.domain commit | +| Day 6 PM | Merge/rebase window | Fixes | B3.cli tests | C2.compiler handoff | Full nox | QA signoff | M1 delta clear | +| Day 7 AM | C5.diff prep | C6.pipeline prep | B3.cli tests | C2.compiler handoff | C5/C6 test scaffolds | QA check | M1 delta close | +| Day 7 PM | M1 buffer + polish | M1 buffer + polish | M1 buffer + polish | C2.compiler handoff | Full nox | QA signoff | M1 verified | + +**Days 8-14** +| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Day 8 AM | C9.execute wiring | C9.execute support | Prep D1 fixtures | C8.providers configs | C8/C9 test scaffolds | QA check | Execute phase draft | +| Day 8 PM | C9.execute finalize | C9.apply prep | D1.domain prep | C8.providers finalize | C9 execute tests | QA check | C8 + C9.execute ready | +| Day 9 AM | C9.apply implementation | C9.apply implementation | D1.domain start | Support C8 docs | C9.apply tests | QA check | Apply flow draft | +| Day 9 PM | C9.apply finalize | Apply review | D1.domain continue | Provider actor polish | Apply robot + nox | QA signoff | Apply flow ready | +| Day 10 AM | D1 review | D1 support | D1.domain model | Support D1 fixtures | D1 test scaffolds | QA check | Decision model draft | +| Day 10 PM | D1 review | D1 support | D1 tests + docs + nox | D1 examples polish | D1 Robot/ASV | QA signoff | D1 commit | +| Day 11 AM | D2 review | D2 support | D2.service record | D2 fixtures | D2 test scaffolds | QA check | Decision recording draft | +| Day 11 PM | D2 review | D2 support | D2 tests + nox | D2 examples | D2 Robot/ASV | QA signoff | D2 commit | +| Day 12 AM | E1 review | E1.domain model | E1 fixtures | E2.actor prep | E1 test scaffolds | QA check | E1 draft | +| Day 12 PM | E1 review | E1 tests + docs + nox | E1 fixtures | E2.actor prep | E1 Robot/ASV | QA signoff | E1 commit | +| Day 13 AM | E2.service | E2.service support | E2 fixtures | E2.actor tool | E2 test scaffolds | QA check | E2 draft | +| Day 13 PM | E2.service tests + nox | E2 support | E2 fixtures | E2.actor tests + nox | E2 Robot/ASV | QA signoff | E2 commit | +| Day 14 AM | Integration triage | Integration support | Integration support | Integration support | Full Robot + Behave | QA signoff | M3 test pass | +| Day 14 PM | Release candidate | Release candidate | Release candidate | Release candidate | Full nox | QA signoff | M3 verified | + +**Days 15-21** +| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Day 15 AM | D5 review | D5 support | D5.db migration | Prep D5 fixtures | D5 test scaffolds | QA check | D5.db draft | +| Day 15 PM | D5 review | D5 support | D5.repo implementation | D5 docs polish | D5 Robot/ASV | QA signoff | D5.db/repo commit | +| Day 16 AM | D3.cli review | D3.cli support | D3.cli implementation | D3 fixtures | D3 test scaffolds | QA check | D3.cli draft | +| Day 16 PM | D3.cli review | D3.cli support | D3.cli tests + nox | D3 examples | D3 Robot/ASV | QA signoff | D3.cli commit | +| Day 17 AM | D4.revert implementation | D4 support | D4 fixtures | D4 docs polish | D4 test scaffolds | QA check | D4.revert draft | +| Day 17 PM | D4.revert tests + nox | D4 support | D4 fixtures | D4 examples | D4 Robot/ASV | QA signoff | D4.revert commit | +| Day 18 AM | D4.append implementation | D5.di support | D5.di wiring | D4/D5 fixtures | D4 append tests | QA check | D4.append draft | +| Day 18 PM | D4.append tests + nox | D5.di support | D5.di tests + nox | D5 docs polish | D5 Robot/ASV | QA signoff | D4.append + D5.di commits | +| Day 19 AM | E3.exec review | E3.exec implementation | E3 fixtures | E3 docs polish | E3 test scaffolds | QA check | E3.exec draft | +| Day 19 PM | E3.exec tests + nox | E3.exec support | E3 fixtures | E3 examples | E3 Robot/ASV | QA signoff | E3.exec commit | +| Day 20 AM | E4.merge implementation | E4.merge support | E4 fixtures | E4 docs polish | E4 test scaffolds | QA check | E4.merge draft | +| Day 20 PM | E4.merge tests + nox | E4.merge support | E4 fixtures | E4 examples | E4 Robot/ASV | QA signoff | E4.merge commit | +| Day 21 AM | Integration triage | Integration support | Integration support | Integration support | Full Robot + Behave | QA signoff | M4 test pass | +| Day 21 PM | Release candidate | Release candidate | Release candidate | Release candidate | Full nox | QA signoff | M4 verified | + +**Days 22-30** +| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Day 22 AM | G1 review | G3.semantic prep | CTX1.index implementation | CTX1 fixtures | CTX1 test scaffolds | QA check | CTX1 draft | +| Day 22 PM | G1 review | G3.semantic prep | CTX1 tests + nox | CTX1 docs polish | CTX1 Robot/ASV | QA signoff | CTX1 commit | +| Day 23 AM | G1 review | G3.semantic implementation | CTX1 index tune | CTX1 fixtures | G3 test scaffolds | QA check | G3 draft | +| Day 23 PM | G1 review | G3 tests + nox | CTX1 finalize | CTX1 docs | G3 Robot/ASV | QA signoff | G3 commit | +| Day 24 AM | G1 review | G2.checkpoint prep | G4.context implementation | G4 fixtures | G4 test scaffolds | QA check | G4 draft | +| Day 24 PM | G1 review | G2.checkpoint prep | G4 tests + nox | G4 docs polish | G4 Robot/ASV | QA signoff | G4 commit | +| Day 25 AM | G1 review | G2.checkpoint implementation | G4 context tune | G4 fixtures | G2 test scaffolds | QA check | G2 draft | +| Day 25 PM | G1 review | G2 tests + nox | G4 finalize | G4 docs | G2 Robot/ASV | QA signoff | G2 commit | +| Day 26 AM | G1.decompose implementation | G3.semantic tuning | G5.estimate prep | G5 fixtures | G1 test scaffolds | QA check | G1 draft | +| Day 26 PM | G1.decompose tests + nox | G3.semantic support | G5.estimate prep | G5 docs polish | G1 Robot/ASV | QA signoff | G1 commit | +| Day 27 AM | G1 performance | G3.semantic performance | G5.estimate implementation | G5 fixtures | G5 test scaffolds | QA check | G5 draft | +| Day 27 PM | G1 performance | G3.semantic support | G5 tests + nox | G5 docs | G5 Robot/ASV | QA signoff | G5 commit | +| Day 28 AM | F0.stubs review | F0.stubs implementation | Integration support | F0 fixtures | F0 test scaffolds | QA check | F0 draft | +| Day 28 PM | F0.stubs review | F0 tests + nox | Integration support | F0 docs polish | F0 Robot/ASV | QA signoff | F0 commit | +| Day 29 AM | M6 perf triage | Perf tuning | Perf tuning | Perf fixtures | Perf tests | QA check | Perf draft | +| Day 29 PM | M6 perf triage | Perf tuning | Perf tuning | Perf docs | Full Robot/ASV | QA signoff | Perf ready | +| Day 30 AM | Integration triage | Integration support | Integration support | Integration support | Full Robot + Behave | QA signoff | M6 test pass | +| Day 30 PM | Release candidate | Release candidate | Release candidate | Release candidate | Full nox | QA signoff | M6 verified | + +**Days 31-35** +| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Day 31 AM | F1 review | F1.client implementation | F4.remote prep | F1 fixtures | F1 test scaffolds | QA check | F1 draft | +| Day 31 PM | F1 review | F1 tests + nox | F4.remote prep | F1 docs polish | F1 Robot/ASV | QA signoff | F1 commit | +| Day 32 AM | F1 review | F1.client finalize | F4.remote prep | F1 fixtures | F1 test scaffolds | QA check | F1 finalize | +| Day 32 PM | F2 sync review | F2.sync implementation | F4.remote prep | F2 fixtures | F2 Robot/ASV | QA signoff | F2 draft | +| Day 33 AM | F2 sync review | F2 tests + nox | F4.remote implementation | F2 docs polish | F2 test scaffolds | QA check | F2 commit | +| Day 33 PM | F3.ws review | F3.ws implementation | F4.remote implementation | F3 fixtures | F3 Robot/ASV | QA signoff | F3 draft | +| Day 34 AM | F3.ws review | F3 tests + nox | F4.remote tests + nox | F3 docs polish | F3 test scaffolds | QA check | F3 commit | +| Day 34 PM | F4.remote review | F4.remote tests + nox | F4.remote finalize | F4 docs polish | F4 Robot/ASV | QA signoff | F4 commit | +| Day 35 AM | Integration triage | Integration support | Integration support | Integration support | Full Robot + Behave | QA signoff | M7 test pass | +| Day 35 PM | Release candidate | Release candidate | Release candidate | Release candidate | Full nox | QA signoff | M7 verified | + +**Days 36-40** +| Day/Block | Jeff | Luis | Hamza | Aditya | Rui | Brent | Gate | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Day 36 AM | A6.core/A6.service review | A6.service implementation | A6.cli support | A6 examples | A6 test scaffolds | QA check | A6 draft | +| Day 36 PM | A6.core/A6.service tests + nox | A6.service support | A6.cli tests + nox | A6 docs polish | A6 Robot/ASV | QA signoff | A6 commit | +| Day 37 AM | G5.estimate review | G5.estimate support | G5.estimate implementation | G5 fixtures | G5 test scaffolds | QA check | G5 draft | +| Day 37 PM | G5.estimate tests + nox | G5.estimate support | G5.estimate tests + nox | G5 docs polish | G5 Robot/ASV | QA signoff | G5 commit | +| Day 38 AM | G2.checkpoint review | G2.checkpoint implementation | G2 fixtures | G2 docs polish | G2 test scaffolds | QA check | G2 draft | +| Day 38 PM | G2.checkpoint tests + nox | G2.checkpoint support | G2 fixtures | G2 docs | G2 Robot/ASV | QA signoff | G2 commit | +| Day 39 AM | G1.decompose tuning | G3.semantic tuning | Perf fixtures | Perf docs | Perf tests | QA check | Perf draft | +| Day 39 PM | G1.decompose tests + nox | G3.semantic tests + nox | Perf fixtures | Perf docs | Perf Robot/ASV | QA signoff | Perf ready | +| Day 40 AM | Integration triage | Integration support | Integration support | Integration support | Full Robot + Behave | QA signoff | M8 test pass | +| Day 40 PM | Release candidate | Release candidate | Release candidate | Release candidate | Full nox | QA signoff | M8 verified | +- Parallelism focus: Jeff to drive A2b/A4b/A5.legacy + C0.domain; Luis to start A5.beta/A5.gamma; Hamza to start B1.core + B2.persistence; Aditya to start C1 schema/examples; Rui to start test scaffolding for A2b/A4b/A5/B1; Brent to land Q0-Minimum gates. +- Individual status: Jeff (critical path unblocker, heavy load), Luis (persistence architecture), Hamza (resource registry + project model), Aditya (actor YAML/configs), Rui (Behave/Robot/ASV scaffolding), Brent (nox/coverage/CI gates), Mike/Brian (idle/standby). + ## Implementation Checklist This comprehensive checklist tracks all implementation tasks for the CleverAgents project. Each phase item includes mandatory **Code**, **Document**, and **Tests** bullets. Only mark the parent complete when every sub-bullet (including any spawned `Fix – …` remediation tasks) is checked. @@ -660,22 +603,23 @@ This comprehensive checklist tracks all implementation tasks for the CleverAgent Execute all required tests through the appropriate `nox` sessions—never call `behave`, `robot`, or other runners directly. After touching **any** subtask, immediately add discoveries to the Notes section and update task descriptions. +**Commit Ownership Rule**: Each **COMMIT** item has exactly one owner. Every subtask (Code/Docs/Tests/Quality/Commit) must list that same owner in brackets. If a subtask truly requires a different owner, split it into a separate **COMMIT** item under the appropriate parallel group. + ### Updated Team Assignments (by Expertise) -| Developer | Strengths | Assignment Focus | Availability | -| -------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------- | -| **Jeff** | CTO, fastest developer, expert in everything | Critical path blockers, architecture, complex integrations, decision correction, unblocking others | PRIMARY - Available for all critical work | -| **Aditya** | Domain expert (agents/LLMs), understands hierarchical configs | Actor YAML configurations, hierarchical actor graphs, strategy/execution actors, MCP integration | HIGH - Primary on actor/skill work | -| **Rui** | Fastest developer, new to Python | Testing (Behave/Robot), simple implementations, CLI scaffolding, test fixtures | HIGH - Parallel testing track | -| **Brent** | Slow but detail-oriented | Days 1-3: Automated quality gates setup; Days 4-8: Selective review; After Day 8: Validation testing | CONTINUOUS - Independent QA track | -| **Hamza** | RDF expert, Python proficient, no LLM experience | Projects, resources, sandbox, database infrastructure, decision models, context indexing | HIGH - Infrastructure lead | -| **Luis** | Best Python architect, pedantic | Algorithms, state machines, service layer architecture, change tracking, validation pipelines | HIGH - Architecture/service layer | -| **Mike/Brian** | Sysadmins | Deployment only (minimal coding tasks) | LOW - Only deployment tasks | +| Developer | Strengths | Assignment Focus | Availability | +|-----------|-----------|------------------|--------------| +| **Jeff** | CTO, fastest developer, expert in everything | Critical path blockers, architecture, complex integrations, decision correction, unblocking others | PRIMARY - Available for all critical work | +| **Aditya** | Domain expert (agents/LLMs), understands hierarchical configs | Actor YAML configurations, hierarchical actor graphs, strategy/execution actors, MCP integration | HIGH - Primary on actor/skill work | +| **Rui** | Fastest developer, new to Python | Testing (Behave/Robot), simple implementations, CLI scaffolding, test fixtures | HIGH - Parallel testing track | +| **Brent** | Slow but detail-oriented | Days 1-3: Automated quality gates setup; Days 4-8: Selective review; After Day 8: Validation testing | CONTINUOUS - Independent QA track | +| **Hamza** | RDF expert, Python proficient, no LLM experience | Projects, resources, sandbox, database infrastructure, decision models, context indexing | HIGH - Infrastructure lead | +| **Luis** | Best Python architect, pedantic | Algorithms, state machines, service layer architecture, change tracking, validation pipelines | HIGH - Architecture/service layer | +| **Mike/Brian** | Sysadmins | Deployment only (minimal coding tasks) | LOW - Only deployment tasks | ### Work Assignment Philosophy **Jeff** should be assigned to: - - Any task that is blocking other developers - Complex integrations requiring deep architectural understanding - Decision correction mechanism (critical for 30-day goal) @@ -695,22 +639,21 @@ Execute all required tests through the appropriate `nox` sessions—never call ` | **Day 6** | C4.search, C4.git | Search + git skills | Actor compilation (C2.compiler) | | **Day 7** | C6.gating, C9.execute, C9.apply | Plan-actor integration + validation | MVP verification | | **Day 8** | M1.1-M1.10 | MVP merge point coordination | Release v0.1.0-rc1 | -| **Day 15-16** | D4.1 | Correction Service core algorithm | Decision correction | -| **Day 17** | D4.2 | Sandbox checkpointing | Re-execution | -| **Day 18** | D4.3 | Re-execution from correction point | M4 milestone | -| **Day 19** | D4 integration | Unblock E3 parallelism | Luis (E3) | +| **Day 15-16** | D4.revert | Correction Service core algorithm | Decision correction | +| **Day 17** | D4.append | Append correction mode | Re-execution | +| **Day 18** | D5.di | Decision wiring + re-exec | M4 milestone | +| **Day 19** | D5.di | Unblock E3 parallelism | Luis (E3) | | **Day 20-21** | E3 | Parallel execution fine-tuning | Subplan merging | | **Day 22-25** | Deep subplan hierarchies | 5+ level subplan testing | M6 | | **Day 26-28** | Performance optimization | 10K+ file codebase support | Large project autonomy | | **Day 30** | M6.1-M6.10 | Large project merge point | Release v0.3.0 | **BLOCKING CHAIN**: If Jeff is unavailable, the following chains stall: -- A5.1 → A5.5 → A5.7 → Plan persistence (Day 1-2) -- C3.1 → C3.3 → C3.6 → Skill execution (Day 3-6) -- D4.1 → D4.2 → D4.3 → Decision correction (Day 15-18) +- A5.alpha → A5.beta → A5.gamma → Plan persistence (Day 1-2) +- C3.protocol → C3.context → C4.file → Skill execution (Day 3-6) +- D4.revert → D4.append → Decision correction (Day 15-18) **Aditya** should be assigned to: - - ALL actor configuration YAML files and examples - Hierarchical actor graph compositions - Strategy and execution actor templates @@ -719,7 +662,6 @@ Execute all required tests through the appropriate `nox` sessions—never call ` - Anything requiring understanding of LLM tool calling patterns **Luis** should be assigned to: - - State machine implementations (plan lifecycle, phase transitions) - Repository pattern implementations - Service layer architecture @@ -728,7 +670,6 @@ Execute all required tests through the appropriate `nox` sessions—never call ` - Should NOT be assigned to simple CRUD or UI work **Hamza** should be assigned to: - - Database schema design and Alembic migrations - Resource model and sandbox infrastructure - Context indexing and RDF graph store integration @@ -737,7 +678,6 @@ Execute all required tests through the appropriate `nox` sessions—never call ` - Should NOT be assigned to LLM/agent-specific logic **Rui** should be assigned to: - - ALL Behave test scenarios (write BEFORE implementation) - Robot integration tests - Simple CLI scaffolding @@ -745,7 +685,6 @@ Execute all required tests through the appropriate `nox` sessions—never call ` - Should ALWAYS work in parallel with feature developers **Brent** should be assigned to: - - Days 1-3: Setting up comprehensive automated quality gates - Days 4-8: Selective manual review of high-priority items only - After Day 8: High-impact validation and edge case testing with Luis @@ -756,10 +695,11 @@ Execute all required tests through the appropriate `nox` sessions—never call ` | Milestone | Day | Goal | Success Criteria | |-----------|-----|------|------------------| -| **M1: MVP** | Day 7 | Create action → Use on project → Execute with sandbox → Apply changes (source code only) | `agents [--data-dir PATH] [--config-path PATH] action create` + `agents [--data-dir PATH] [--config-path PATH] plan use` + `agents [--data-dir PATH] [--config-path PATH] plan execute` + `agents [--data-dir PATH] [--config-path PATH] plan apply` working end-to-end on a git repository with sandboxed execution | -| **M2: Projects** | Day 10 | Project/Resource CLI working with git worktree sandbox | `agents [--data-dir PATH] [--config-path PATH] project create` + `agents [--data-dir PATH] [--config-path PATH] project add-resource` + git worktree isolation verified | +| **M1: MVP** | Day 7 | Create action → Use on project → Execute with sandbox → Apply changes (source code only) | `agents [--data-dir PATH] [--config-path PATH] action create --config` + `agents [--data-dir PATH] [--config-path PATH] plan use ` + `agents [--data-dir PATH] [--config-path PATH] plan execute` + `agents [--data-dir PATH] [--config-path PATH] plan apply` working end-to-end on a git repository with sandboxed execution | +| **M2: Projects** | Day 10 | Project/Resource CLI working with git worktree sandbox | `agents [--data-dir PATH] [--config-path PATH] project create` + `agents [--data-dir PATH] [--config-path PATH] resource add git-checkout` + `agents [--data-dir PATH] [--config-path PATH] project link-resource` (B3.cli) + git worktree isolation verified | | **M3: Actors** | Day 14 | Full plan lifecycle with actors, skills, multi-file generation | Actor YAML parsed → LangGraph compiled → Skills executed → Multi-file ChangeSet produced → Validation passing → Applied | | **M4: Decisions** | Day 21 | Decision recording, tree viewing, correction mechanism | `agents [--data-dir PATH] [--config-path PATH] plan tree` shows decisions → `agents [--data-dir PATH] [--config-path PATH] plan explain` works → `agents [--data-dir PATH] [--config-path PATH] plan correct --mode=revert` re-executes from correction point | +| **M4: Decisions** | Day 21 | Invariant CLI usage | `agents [--data-dir PATH] [--config-path PATH] invariant add --project ""` + `agents [--data-dir PATH] [--config-path PATH] invariant list --project ` | | **M5: Subplans** | Day 25 | Hierarchical subplans with parallel execution and merging | Parent plan spawns 5+ subplans → Parallel execution → Three-way merge → Validation passes | | **M6: Large Projects** | Day 30 | Handle 10,000+ file projects, autonomous language porting | Can port a 500-file Python module to TypeScript using hierarchical decomposition with decision correction | | **Server Connectivity** | Beyond Day 30 | Client interfaces for server communication; server developed independently | Client-to-server API abstractions defined, but local execution only; no server implementation in this project | @@ -774,7 +714,6 @@ Execute all required tests through the appropriate `nox` sessions—never call ` 4. **Focus on Core Functionality**: All effort goes to plan lifecycle, actors, skills, sandboxing, decisions, and subplans The following Section 8 (Server Connectivity) items are explicitly **OUT OF SCOPE** for the 30-day deadline: - - Client-to-server API integration - WebSocket streaming to server - Remote project execution via server @@ -788,43 +727,36 @@ The following Section 8 (Server Connectivity) items are explicitly **OUT OF SCOP The following chains represent sequential dependencies where each item MUST complete before the next can start: **CHAIN 1: Data Layer (Days 1-3)** - ``` A5.alpha (DB migrations) → A5.beta (ORM models) → A5.gamma (repos/service/DI) ``` **CHAIN 2: Resource Layer (Days 2-4)** - ``` B1.core (Domain Models) → B2.persistence (DB tables) → B2.service (Services) → B3.cli (CLI) ``` **CHAIN 3: Sandbox Layer (Days 3-5)** - ``` B4.sandbox (Strategy + Manager) → B4.sandbox git_worktree → B4.sandbox copy_on_write ``` **CHAIN 4: Actor Layer (Days 4-7)** - ``` C1.schema (Actor Schema) → C2.legacy (Drop v2 configs) → C2.loader (Actor Loader) → C2.compiler (Actor Compiler) → C2.refs (Reference Resolution) ``` **CHAIN 5: Skill Layer (Days 5-8)** - ``` C3.protocol (Skill Protocol) → C3.context (Skill Context) → C3.inline (Inline Executor) → C4.file/C4.search/C4.git (Built-in Skills) ``` **CHAIN 6: Change Tracking (Days 6-9)** - ``` C5.model (Change Models) → C5.router (Tool Router) → C5.diff (Diff Generator) ``` **CHAIN 7: Plan-Actor Integration (Days 8-14)** - ``` C9.execute (Strategize/Execute) → C6.pipeline/C6.gating (Validation) → C9.apply (Apply + Review) ``` @@ -898,7 +830,7 @@ MERGE POINT: Day 8 - All tracks converge for MVP verification - Coverage must be >=97% - Brent signs off on quality - Jeff leads integration testing - + MERGE POINT DAY 8 - EXPLICIT COORDINATION TASKS: ├── [Jeff - 9:00 AM] M1.1: Run full `nox` test suite, collect failures ├── [Jeff - 10:00 AM] M1.2: Verify Plan persistence (A5) connects to CLI (A4) @@ -1042,14 +974,12 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - **Workstream T**: Testing [Rui] - CONTINUOUS **Merge Points**: - - **Day 7 (M1)**: All workstreams coordinate for MVP verification - **Day 14 (M3)**: Full plan lifecycle integration - **Day 21 (M4)**: Decision tree and correction mechanism - **Day 30 (M6)**: Large project autonomy target **MERGE POINT DAY 14 (M3) - Full Plan Lifecycle Integration**: - ``` ├── [Jeff - 9:00 AM] M3.1: Verify full plan lifecycle end-to-end │ └── Test: action create → plan use → plan execute → plan apply @@ -1069,7 +999,6 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target ``` **MERGE POINT DAY 21 (M4) - Decision Tree & Correction**: - ``` ├── [Hamza - 9:00 AM] M4.1: Verify decision recording captures context │ └── Test: Execute strategy phase, verify decisions recorded with snapshots @@ -1094,48 +1023,11 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Target: Days 0-3 (Minimum gates before merges; advanced gates after M1)** -**CRITICAL**: This MUST be completed before other workstreams begin to ensure all code meets quality standards from the start. +**Parallelization rules**: +- Minimum gating (pre-commit + CI + coverage enforcement) is a merge blocker; it can run in parallel with Week 1 coding but must land before any feature branches merge. +- Advanced automation (complexity metrics, dashboards, extended security scanning) is deferred until after M1 to avoid blocking the MVP. -- [ ] **Stage Q0: Pre-commit Hooks Setup** (Day 1) **[Brent - CRITICAL]** - - [ ] Code: Create automated pre-commit quality checks - - [ ] **Q0.1** [Brent] Install and configure pre-commit framework: - - [ ] **Q0.1a** Add `pre-commit>=3.6.0` to `pyproject.toml` dev dependencies - - [ ] **Q0.1b** Create `.pre-commit-config.yaml` in project root - - [ ] **Q0.1c** Add branch protection hook to prevent commits to main - - [ ] Commit: "feat(qa): add pre-commit framework" - - [ ] **Q0.2** [Brent] Configure Ruff for formatting and linting: - - [ ] **Q0.2a** Add Ruff formatting hook with auto-fix - - [ ] **Q0.2b** Add Ruff linting hook with auto-fix for safe fixes - - [ ] **Q0.2c** Test with intentionally bad code - - [ ] Commit: "feat(qa): add ruff formatting and linting hooks" - - [ ] **Q0.3** [Brent] Add pyright type checking: - - [ ] **Q0.3a** Configure pyright hook for changed Python files - - [ ] **Q0.3b** Set to run serially (slow but thorough) - - [ ] **Q0.3c** Test with code containing type errors - - [ ] Commit: "feat(qa): add pyright type checking hook" - - [ ] **Q0.4** [Brent] Add security scanning: - - [ ] **Q0.4a** Add `bandit[toml]>=1.7.5` to dev dependencies - - [ ] **Q0.4b** Configure bandit in `pyproject.toml` - - [ ] **Q0.4c** Add bandit pre-commit hook - - [ ] **Q0.4d** Add semgrep with custom rules for eval/exec detection - - [ ] Commit: "feat(qa): add security scanning hooks" - - [ ] **Q0.5** [Brent] Add code quality checks: - - [ ] **Q0.5a** Add `vulture>=2.10` for dead code detection - - [ ] **Q0.5b** Configure vulture whitelist for false positives - - [ ] **Q0.5c** Add commit message linting for conventional commits - - [ ] Commit: "feat(qa): add code quality hooks" - - [ ] **Q0.6** [Brent] Create developer setup automation: - - [ ] **Q0.6a** Create `scripts/setup-dev.sh` to install pre-commit - - [ ] **Q0.6b** Update README.md with setup instructions - - [ ] **Q0.6c** Test on fresh checkout - - [ ] Commit: "feat(qa): add developer setup script" - - [ ] Tests: Verify all hooks work correctly - - [ ] **Q0.7** [Rui] Write script to test all pre-commit hooks: - - [ ] Test formatting fixes - - [ ] Test linting catches issues - - [ ] Test type checking blocks bad types - - [ ] Test security scanning catches eval() - - [ ] Commit: "test(qa): add pre-commit hook tests" +**Parallel Group Q0-Minimum Gates [Brent - blocks merges]** - [ ] **COMMIT (Owner: Brent | Group: Q0-Minimum) - Commit message: "feat(qa): add pre-commit baseline hooks"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Brent]: Add `pre-commit>=3.6.0` to `pyproject.toml` dev dependencies and ensure it is included in the `dev` extra. @@ -1144,10 +1036,11 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Brent]: Add/confirm `nox -s lint` session that runs Ruff + pyright using project settings; ensure session exits non-zero on warnings. - [ ] Code [Brent]: Add/confirm `nox -s format` session for Ruff formatting and align it with pre-commit `ruff format` behavior. - [ ] Docs [Brent]: Update `CONTRIBUTING.md` with pre-commit install + run steps (no helper scripts). - - [ ] Tests (Behave) [Rui]: Add scenarios in `features/quality_automation.feature` that parse `.pre-commit-config.yaml`, assert required hooks are present, and verify pinned versions. - - [ ] Tests (Robot) [Rui]: Add `robot/quality_automation.robot` that runs `nox -s lint` and asserts zero failures. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/precommit_config_bench.py` to benchmark config parsing and hook list extraction. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Brent]: Add scenarios in `features/quality_automation.feature` that parse `.pre-commit-config.yaml`, assert required hooks are present, and verify pinned versions. + - [ ] Tests (Robot) [Brent]: Add `robot/quality_automation.robot` that runs `nox -s lint` and asserts zero failures. + - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/precommit_config_bench.py` to benchmark config parsing and hook list extraction. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Brent]: `git commit -m "feat(qa): add pre-commit baseline hooks"`. - [ ] **COMMIT (Owner: Brent | Group: Q0-Minimum) - Commit message: "feat(ci): add nox-based PR validation workflow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) @@ -1155,20 +1048,22 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Brent]: Ensure CI uses Python 3.13, caches pip/Hatch artifacts, and uploads `nox` logs on failure. - [ ] Code [Brent]: Fail pipeline if any `nox` session fails or coverage <97% (explicit coverage gate). - [ ] Docs [Brent]: Add CI usage notes in `docs/development/ci-cd.md`, including local repro commands and cache notes. - - [ ] Tests (Behave) [Rui]: Add a scenario that validates the workflow file exists and references required `nox` sessions. - - [ ] Tests (Robot) [Rui]: Add a Robot smoke test that runs the same `nox` session matrix locally and asserts zero failures. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/ci_yaml_parse_bench.py` to benchmark workflow parsing and key lookup. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Brent]: Add a scenario that validates the workflow file exists and references required `nox` sessions. + - [ ] Tests (Robot) [Brent]: Add a Robot smoke test that runs the same `nox` session matrix locally and asserts zero failures. + - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/ci_yaml_parse_bench.py` to benchmark workflow parsing and key lookup. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Brent]: `git commit -m "feat(ci): add nox-based PR validation workflow"`. - [ ] **COMMIT (Owner: Brent | Group: Q0-Minimum) - Commit message: "feat(qa): enforce coverage >=97%"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Brent]: Update `nox -s coverage_report` (or equivalent session) to fail when coverage <97% and emit a clear error message. - [ ] Code [Brent]: Wire coverage threshold enforcement into CI summary output (explicit failure line for parsing). - [ ] Docs [Brent]: Update `docs/development/testing.md` with new coverage requirement and sample output. - - [ ] Tests (Behave) [Rui]: Add a scenario that parses coverage config and asserts threshold >=97%. - - [ ] Tests (Robot) [Rui]: Add a Robot test that runs `nox -s coverage_report` and asserts pass/fail behavior. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/coverage_report_bench.py` for coverage report runtime baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Brent]: Add a scenario that parses coverage config and asserts threshold >=97%. + - [ ] Tests (Robot) [Brent]: Add a Robot test that runs `nox -s coverage_report` and asserts pass/fail behavior. + - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/coverage_report_bench.py` for coverage report runtime baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Brent]: `git commit -m "feat(qa): enforce coverage >=97%"`. **Parallel Group Q0-Advanced Gates [Brent - AFTER M1]** @@ -1178,84 +1073,87 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Brent]: Add pre-commit hooks for Bandit + Semgrep with minimal safe ruleset and explicit exclude patterns. - [ ] Code [Brent]: Add `nox -s security` session that runs Bandit + Semgrep with config files. - [ ] Docs [Brent]: Document security scan expectations in `docs/development/quality-automation.md`. - - [ ] Tests (Behave) [Rui]: Add scenario verifying Bandit/Semgrep hooks are declared in `.pre-commit-config.yaml`. - - [ ] Tests (Robot) [Rui]: Add Robot test that runs `nox -s security` (create session if missing). - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_scan_bench.py` to baseline scan runtime. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Brent]: Add scenario verifying Bandit/Semgrep hooks are declared in `.pre-commit-config.yaml`. + - [ ] Tests (Robot) [Brent]: Add Robot test that runs `nox -s security` (create session if missing). + - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/security_scan_bench.py` to baseline scan runtime. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Brent]: `git commit -m "feat(qa): add security scanning hooks"`. - [ ] **COMMIT (Owner: Brent | Group: Q0-Advanced) - Commit message: "feat(qa): add complexity monitoring"** (After M1) - [ ] Code [Brent]: Add `radon>=6.0.1` and a `nox -s complexity` session with threshold <=10. - [ ] Code [Brent]: Add complexity check to CI matrix (non-blocking until M3) and print summary. - [ ] Docs [Brent]: Document complexity thresholds and exceptions policy. - - [ ] Tests (Behave) [Rui]: Add scenario that asserts radon configuration exists. - - [ ] Tests (Robot) [Rui]: Add Robot test that runs `nox -s complexity` on a fixture module. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/complexity_scan_bench.py` for radon runtime baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Brent]: Add scenario that asserts radon configuration exists. + - [ ] Tests (Robot) [Brent]: Add Robot test that runs `nox -s complexity` on a fixture module. + - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/complexity_scan_bench.py` for radon runtime baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Brent]: `git commit -m "feat(qa): add complexity monitoring"`. - [ ] **COMMIT (Owner: Brent | Group: Q0-Advanced) - Commit message: "docs(qa): add quality automation guide"** (After M1) - [ ] Docs [Brent]: Create `docs/development/quality-automation.md` with hook lists, CI steps, and troubleshooting. - [ ] Docs [Brent]: Link the guide from `README.md` and `CONTRIBUTING.md`. - - [ ] Tests (Behave) [Rui]: Add scenario verifying the guide exists and is linked. - - [ ] Tests (Robot) [Rui]: Add Robot doc build smoke test via `nox -s docs`. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/docs_build_bench.py` for docs build runtime baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Brent]: Add scenario verifying the guide exists and is linked. + - [ ] Tests (Robot) [Brent]: Add Robot doc build smoke test via `nox -s docs`. + - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/docs_build_bench.py` for docs build runtime baseline. + - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Brent]: `git commit -m "docs(qa): add quality automation guide"`. --- ### Section 1: Completed Foundation (Phases 0-1) [PRESERVED] -- [x] Phase 0: Discovery and Requirements Elaboration - - [x] Code: Implement Python discovery tooling for CLI, server, data contracts, supporting assets, environment variables, implicit behaviors, and parity matrix generation. - - [x] Document: Append findings, scripts, and open questions to **Phase 0 Notes** with `file_path:line_number` references. - - [x] Tests: Run Behave discovery scenarios and Robot smoke suites covering the generated artifacts. +- [X] Phase 0: Discovery and Requirements Elaboration + - [X] Code: Implement Python discovery tooling for CLI, server, data contracts, supporting assets, environment variables, implicit behaviors, and parity matrix generation. + - [X] Document: Append findings, scripts, and open questions to **Phase 0 Notes** with `file_path:line_number` references. + - [X] Tests: Run Behave discovery scenarios and Robot smoke suites covering the generated artifacts. -- [x] Phase 1: Architecture Definition - - [x] Code: Produce ADR stubs, module scaffolding, coding standard configurations, and packaging setup. - - [x] ADR-001: Python Package Layering and Module Boundaries - - [x] ADR-002: Asyncio Concurrency Model - - [x] ADR-003: Dependency Injection Framework - - [x] ADR-004: Pydantic for Data Validation - - [x] ADR-005: Error Handling Hierarchy - - [x] ADR-006: CLEVERAGENTS Environment Variables - - [x] ADR-007: Repository Pattern for Persistence - - [x] ADR-008: Provider Plugin Architecture - - [x] ADR-009: CLI Framework Selection - - [x] ADR-010: Logging and Observability - - [x] Document: Update **Phase 1 Notes** with ADR locations, dependency policies, and style guides. - - [x] Tests: Execute Ruff, pyright, Behave architecture scenarios, and Robot lint pipelines. +- [X] Phase 1: Architecture Definition + - [X] Code: Produce ADR stubs, module scaffolding, coding standard configurations, and packaging setup. + - [X] ADR-001: Python Package Layering and Module Boundaries + - [X] ADR-002: Asyncio Concurrency Model + - [X] ADR-003: Dependency Injection Framework + - [X] ADR-004: Pydantic for Data Validation + - [X] ADR-005: Error Handling Hierarchy + - [X] ADR-006: CLEVERAGENTS Environment Variables + - [X] ADR-007: Repository Pattern for Persistence + - [X] ADR-008: Provider Plugin Architecture + - [X] ADR-009: CLI Framework Selection + - [X] ADR-010: Logging and Observability + - [X] Document: Update **Phase 1 Notes** with ADR locations, dependency policies, and style guides. + - [X] Tests: Execute Ruff, pyright, Behave architecture scenarios, and Robot lint pipelines. --- ### Section 2: Preserved Work from Previous Implementation [PRESERVED] -- [x] LangChain/LangGraph Foundation - - [x] Dependencies installed (langchain, langgraph, langsmith, etc.) - - [x] ADR-011: LangChain/LangGraph Integration Patterns - - [x] Base StateGraph classes (BaseAgent, BaseStateGraph) - - [x] LangChain mock provider (FakeListLLM) +- [X] LangChain/LangGraph Foundation + - [X] Dependencies installed (langchain, langgraph, langsmith, etc.) + - [X] ADR-011: LangChain/LangGraph Integration Patterns + - [X] Base StateGraph classes (BaseAgent, BaseStateGraph) + - [X] LangChain mock provider (FakeListLLM) -- [x] Core LangGraph Workflows - - [x] PlanGenerationGraph (load_context -> analyze_requirements -> generate_plan -> validate) - - [x] ContextAnalysisAgent (5-node workflow for context analysis) - - [x] AutoDebugGraph (analyze_error -> generate_fix -> validate_fix -> apply_fix) - - [x] Memory service with EntityMemory - - [x] CLI streaming integration +- [X] Core LangGraph Workflows + - [X] PlanGenerationGraph (load_context -> analyze_requirements -> generate_plan -> validate) + - [X] ContextAnalysisAgent (5-node workflow for context analysis) + - [X] AutoDebugGraph (analyze_error -> generate_fix -> validate_fix -> apply_fix) + - [X] Memory service with EntityMemory + - [X] CLI streaming integration -- [x] Provider Integration - - [x] Provider registry - - [x] OpenAI, Anthropic, Google, OpenRouter adapters - - [x] LangSmith observability +- [X] Provider Integration + - [X] Provider registry + - [X] OpenAI, Anthropic, Google, OpenRouter adapters + - [X] LangSmith observability -- [x] Actor System (Stage 7.5) - - [x] Actor domain model with config hashing - - [x] Actor persistence (database, repository) - - [x] Actor registry (built-ins from provider registry) - - [x] Actor CLI commands (add, update, remove, list, show) - - [x] Actor-first plan/chat commands (--actor flag) - - [x] v2 format compatibility for actor configs +- [X] Actor System (Stage 7.5) + - [X] Actor domain model with config hashing + - [X] Actor persistence (database, repository) + - [X] Actor registry (built-ins from provider registry) + - [X] Actor CLI commands (add, update, remove, list, show) + - [X] Actor-first plan/chat commands (--actor flag) + - [X] v2 format compatibility for actor configs --- @@ -1265,14 +1163,14 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **WEEK 1 - CRITICAL PATH** -- [x] **Stage A1: Plan Data Model** (Day 1) - COMPLETED 2026-02-05 - - [x] Code: Create Plan domain model - - [x] Define `Plan` Pydantic model with fields (plan_id ULID, parent_plan_id, root_plan_id, attempt counter, phase, state, timestamps) - - [x] Define `PlanPhase` enum (Action, Strategize, Execute, Apply, Applied) - - [x] Define `PlanState` enum per phase (available, draft, archived, queued, processing, errored, complete, cancelled) - - [x] Add namespace support to plan naming (`[server:][namespace/]`) - - [x] Location: `src/cleveragents/domain/models/core/plan.py` - - [x] Tests: Behave scenarios for plan model validation, phase/state transitions (30 scenarios in `features/plan_model.feature`) +- [X] **Stage A1: Plan Data Model** (Day 1) - COMPLETED 2026-02-05 + - [X] Code: Create Plan domain model + - [X] Define `Plan` Pydantic model with fields (plan_id ULID, parent_plan_id, root_plan_id, attempt counter, phase, state, timestamps) + - [X] Define `PlanPhase` enum (Action, Strategize, Execute, Apply, Applied) + - [X] Define `PlanState` enum per phase (available, draft, archived, queued, processing, errored, complete, cancelled) + - [X] Add namespace support to plan naming (`[server:][namespace/]`) + - [X] Location: `src/cleveragents/domain/models/core/plan.py` + - [X] Tests: Behave scenarios for plan model validation, phase/state transitions (30 scenarios in `features/plan_model.feature`) - [X] **Stage A2: Action Model** (Day 1) - COMPLETED 2026-02-05 - [X] Code: Create Action domain model @@ -1290,31 +1188,39 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Jeff]: Add `automation_profile` field (namespaced name string) to `Action` and validate `/` format + `local/` default handling. - [ ] Code [Jeff]: Add `invariant_actor` (optional actor ref) and `invariants` list (action-scoped) with trimming, de-duplication, and empty-string rejection. - [ ] Code [Jeff]: Add `definition_of_done_template` to preserve the pre-rendered DoD string before arg substitution; keep `definition_of_done` as rendered output. + - [ ] Code [Jeff]: Default `definition_of_done_template` to the original `definition_of_done` when omitted, and ensure Pydantic round-trip (model_dump/model_validate) preserves both fields. + - [ ] Code [Jeff]: Add `Action.render_definition_of_done()` that renders the template using validated args and raises explicit errors on missing keys. - [ ] Code [Jeff]: Extend `ActionArgument` validation to enforce `min_value <= max_value`, regex only for string args, and default value type checks; reject defaults that violate regex. - [ ] Code [Jeff]: Add `ActionArgument.coerce_value()` helper that converts CLI/YAML strings into typed values (int/float/bool/list) with clear errors. + - [ ] Code [Jeff]: Add `ActionArgument.from_mapping()` to parse YAML argument dicts (name/type/required/description/default/min/max/regex) and normalize them into ActionArgument instances. - [ ] Code [Jeff]: Add `Action.to_template_context()` (or equivalent) to generate deterministic arg context for templating and preserve ordering for tests. + - [ ] Code [Jeff]: Add `Action.from_config()` to build an Action from YAML config + CLI overrides with stable argument ordering for deterministic tests. + - [ ] Code [Jeff]: Update `ActionArgument.__str__()` to surface default/min/max/regex when emitting diagnostics or CLI output. - [ ] Code [Jeff]: Update `Action.validate_arguments()` to use default values when optional args are omitted and to include regex/min/max checks in error output. - [ ] Code [Jeff]: Update `PlanLifecycleService.create_action()` to accept invariants, invariant_actor, automation_profile, and definition_of_done_template and pass them into the domain model. - [ ] Docs [Jeff]: Update or create `docs/reference/action_model.md` with new fields, YAML-first guidance, and invariants/automation profile semantics. - - [ ] Tests (Behave) [Rui]: Add scenarios in `features/action_model.feature` for invariants validation, automation profile parsing, definition_of_done_template retention, and default value coercion. - - [ ] Tests (Robot) [Rui]: Add Robot scenario that loads action YAML and asserts invariants/automation profile fields are surfaced in CLI output (wired in A4b). - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_model_bench.py` to benchmark action argument parsing + template rendering. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add scenarios in `features/action_model.feature` for invariants validation, automation profile parsing, definition_of_done_template retention, and default value coercion. + - [ ] Tests (Robot) [Jeff]: Add Robot scenario that loads action YAML and asserts invariants/automation profile fields are surfaced in CLI output (wired in A4b). + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/action_model_bench.py` to benchmark action argument parsing + template rendering. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(domain): align action metadata with invariants and automation profiles"`. - [ ] **COMMIT (Owner: Luis | Group: A2b.beta) - Commit message: "feat(domain): align plan metadata with action linkage and automation profiles"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Luis]: Add `action_name` (namespaced name string) and `action_id` (ULID string) to `Plan` for traceability to the originating action. + - [ ] Code [Luis]: Replace `project_ids` with `project_names` (namespaced names) and introduce `ProjectLink` structure (name, alias, read_only) to preserve link metadata per plan. + - [ ] Code [Luis]: Add validators to enforce namespaced project names, unique aliases, and stable ordering for CLI display. - [ ] Code [Luis]: Add `automation_profile`, `invariant_actor`, and `invariants` fields to `Plan` with explicit source tags (action/project/plan/global) and ordering rules. - [ ] Code [Luis]: Add `arguments` map (validated JSON-serializable values) and `definition_of_done_template` capture to the plan model for later re-rendering. - [ ] Code [Luis]: Add execution metadata placeholders: `changeset_id`, `sandbox_refs`, `validation_summary`, and `decision_root_id` (optional until later stages). - [ ] Code [Luis]: Add `Plan.validate_immutable_fields()` to enforce automation profile immutability after `plan use` and when phase progresses beyond Strategize. - - [ ] Code [Luis]: Update `PlanLifecycleService.use_action()` to populate action linkage, arguments, invariants, and automation profile on the Plan. + - [ ] Code [Luis]: Update `PlanLifecycleService.use_action()` to populate action linkage, arguments, invariants, automation profile, and project link metadata on the Plan. + - [ ] Code [Luis]: Update `_print_lifecycle_plan` in `src/cleveragents/cli/commands/plan.py` to render project names/aliases instead of raw IDs. - [ ] Docs [Luis]: Update `docs/reference/plan_model.md` to document new fields, immutability rules, and action linkage. - - [ ] Tests (Behave) [Rui]: Add scenarios in `features/plan_model.feature` for automation profile lock, invariant persistence, action linkage fields, and argument serialization. - - [ ] Tests (Robot) [Rui]: Add Robot scenario to inspect `plan status` output for action linkage and automation profile fields (once CLI aligned). - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_model_bench.py` for plan validation and serialization. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add scenarios in `features/plan_model.feature` for automation profile lock, invariant persistence, action linkage fields, and argument serialization. + - [ ] Tests (Robot) [Luis]: Add Robot scenario to inspect `plan status` output for action linkage and automation profile fields (once CLI aligned). + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/plan_model_bench.py` for plan validation and serialization. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(domain): align plan metadata with action linkage and automation profiles"`. - [ ] **COMMIT (Owner: Aditya | Group: A2b.gamma) - Commit message: "docs(action): add action YAML schema and examples"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Docs [Aditya]: Author `docs/schema/action.schema.yaml` with versioning, required fields, and explicit type constraints for actors, invariants, arguments, and automation profiles. @@ -1322,74 +1228,81 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Aditya]: Add schema validation helper in `src/cleveragents/action/schema.py` that loads YAML, validates schema version, and returns typed data. - [ ] Code [Aditya]: Add clear error messages for missing required fields, invalid namespaced names, and invalid argument type combos. - [ ] Code [Aditya]: Add unit helper to normalize YAML keys (snake_case vs camelCase) before validation. - - [ ] Tests (Behave) [Rui]: Add scenarios that load each example YAML and assert schema validation passes; add invalid schema cases (missing actor, invalid namespaced name, bad arg types). - - [ ] Tests (Robot) [Rui]: Add a Robot smoke test that reads example YAML files and reports parse success/failure. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_schema_bench.py` for YAML schema validation throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Aditya]: Add scenarios that load each example YAML and assert schema validation passes; add invalid schema cases (missing actor, invalid namespaced name, bad arg types). + - [ ] Tests (Robot) [Aditya]: Add a Robot smoke test that reads example YAML files and reports parse success/failure. + - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/action_schema_bench.py` for YAML schema validation throughput. + - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Aditya]: `git commit -m "docs(action): add action YAML schema and examples"`. -- [x] **Stage A3: Plan State Machine** (Day 1-2) - COMPLETED 2026-02-05 - - [x] Code: Implement plan lifecycle state machine - - [x] Create `PlanLifecycleService` with phase transition methods - - [x] Implement `create_action()` - creates plan in Action phase - - [x] Implement `use_action(action, projects, args)` - transitions to Strategize - - [x] Implement `execute_plan()` - transitions to Execute - - [x] Implement `apply_plan()` - transitions to Applied - - [x] Add validation for phase transitions (only valid transitions allowed) - - [x] Location: `src/cleveragents/application/services/plan_lifecycle_service.py` - - [x] Tests: Behave scenarios for all phase transitions, invalid transition errors (29 scenarios in `features/plan_lifecycle_service.feature`) +- [X] **Stage A3: Plan State Machine** (Day 1-2) - COMPLETED 2026-02-05 + - [X] Code: Implement plan lifecycle state machine + - [X] Create `PlanLifecycleService` with phase transition methods + - [X] Implement `create_action()` - creates plan in Action phase + - [X] Implement `use_action(action, projects, args)` - transitions to Strategize + - [X] Implement `execute_plan()` - transitions to Execute + - [X] Implement `apply_plan()` - transitions to Applied + - [X] Add validation for phase transitions (only valid transitions allowed) + - [X] Location: `src/cleveragents/application/services/plan_lifecycle_service.py` + - [X] Tests: Behave scenarios for all phase transitions, invalid transition errors (29 scenarios in `features/plan_lifecycle_service.feature`) - [X] **Stage A4: Plan CLI Commands** (Day 2-3) - IN PROGRESS 2026-02-05 - [X] Code: Implement plan lifecycle CLI - - [X] `agents [--data-dir PATH] [--config-path PATH] action create --name --strategy-actor --execution-actor --definition-of-done "" [--arg ...]` + - [X] `agents [--data-dir PATH] [--config-path PATH] action create --config [] [--strategy-actor ] [--execution-actor ] [--definition-of-done ""] [--arg ...]` (legacy `--name` syntax kept in historical notes) - [X] `agents [--data-dir PATH] [--config-path PATH] action list` - list available actions - [X] `agents [--data-dir PATH] [--config-path PATH] action show ` - show action details - [X] `agents [--data-dir PATH] [--config-path PATH] action available ` - make action available - [X] `agents [--data-dir PATH] [--config-path PATH] action archive ` - archive action - - [X] `agents [--data-dir PATH] [--config-path PATH] plan use --project [--arg name=value ...]` - create plan from action + - [X] `agents [--data-dir PATH] [--config-path PATH] plan use [--arg name=value ...]` - create plan from action (legacy `--project` syntax kept in historical notes) - [X] `agents [--data-dir PATH] [--config-path PATH] plan execute [plan_id]` - execute current or specified plan - [X] `agents [--data-dir PATH] [--config-path PATH] plan apply [plan_id]` - apply executed plan (v3 lifecycle) - [X] `agents [--data-dir PATH] [--config-path PATH] plan status [plan_id]` - show plan phase/state - - [X] `agents [--data-dir PATH] [--config-path PATH] plan list` - list plans with phases/states + - [X] `agents [--data-dir PATH] [--config-path PATH] plan list [--phase ] [--state ] [--project ] [--action ]` - list plans with filters - [X] `agents [--data-dir PATH] [--config-path PATH] plan cancel ` - cancel non-terminal plan - [X] Location: `src/cleveragents/cli/commands/action.py`, `src/cleveragents/cli/commands/plan.py` - [X] Tests: Behave tests for action CLI (15 scenarios in `features/action_cli.feature`) **Parallel Group A4b: Action/Plan CLI Spec Alignment + Tests (M1-critical)** **PARALLEL SUBTRACK A4b.alpha [Jeff]**: CLI feature alignment **PARALLEL SUBTRACK A4b.beta [Rui]**: Behave + Robot coverage + **SEQUENTIAL MERGE NOTE**: A4b.alpha depends on A2b.alpha + A2b.beta + A2b.gamma; A4b.beta runs after A4b.alpha to lock CLI output fields and error messages. - [ ] **COMMIT (Owner: Jeff | Group: A4b.alpha) - Commit message: "feat(cli): support action create from YAML config"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Jeff]: Add `--config/-c` to `agents action create`; load YAML via `src/cleveragents/action/schema.py` and fail fast on schema violations. + - [ ] Code [Jeff]: Resolve `--config` paths relative to CWD and emit explicit errors for missing/unreadable files (include path in error). - [ ] Code [Jeff]: Implement override precedence (CLI flags override YAML fields; explicit CLI empty string clears YAML value; CLI omits leave YAML as-is). + - [ ] Code [Jeff]: Validate CLI name vs YAML `name` when both provided; error on mismatch and require YAML `name` when CLI omits it. + - [ ] Code [Jeff]: Use `Action.from_config()` to merge YAML + CLI and preserve deterministic argument ordering for tests. - [ ] Code [Jeff]: Normalize namespaced names, actor refs, and argument definitions from YAML into `ActionArgument` objects; surface errors with field path. - [ ] Code [Jeff]: Add `--update` guard (if action exists) or explicit error per spec; ensure idempotent update path is clear. + - [ ] Code [Jeff]: When `--update` is used, preserve `action_id` and `created_at`, update `updated_at`, and surface action state in output. - [ ] Code [Jeff]: Update `_print_action` output in `src/cleveragents/cli/commands/action.py` to show invariants, invariant_actor, and automation_profile. - [ ] Docs [Jeff]: Update CLI reference with YAML-based action creation + override examples and failure messages. - - [ ] Tests (Behave) [Rui]: Add scenarios in `features/action_cli.feature` covering valid config, overrides, invalid schema, missing required fields, and update conflict. - - [ ] Tests (Robot) [Rui]: Add `robot/action_cli_from_config.robot` with end-to-end CLI flow and output assertions (includes invariants/profile display). - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/action_cli_config_bench.py` for config parsing + normalization. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add scenarios in `features/action_cli.feature` covering valid config, overrides, invalid schema, missing required fields, and update conflict. + - [ ] Tests (Robot) [Jeff]: Add `robot/action_cli_from_config.robot` with end-to-end CLI flow and output assertions (includes invariants/profile display). + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/action_cli_config_bench.py` for config parsing + normalization. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(cli): support action create from YAML config"`. - [ ] **COMMIT (Owner: Jeff | Group: A4b.alpha) - Commit message: "feat(cli): extend plan use with invariants and automation profile"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Jeff]: Change `agents plan use` signature to accept positional `` arguments per spec and keep `--project` as a legacy alias only until A5.legacy removal. - [ ] Code [Jeff]: Add `--automation-profile`, `--invariant`, and `--invariant-actor` flags to `agents plan use` and plumb into PlanLifecycleService. + - [ ] Code [Jeff]: Resolve positional project names via ProjectService; error on missing projects and preserve positional ordering. + - [ ] Code [Jeff]: Parse `--arg name=value` using `ActionArgument.coerce_value()` (not heuristic int/float guessing) and reject unknown args early. - [ ] Code [Jeff]: Resolve action name -> action_id, validate automation profile existence via AutomationProfileService, and attach plan-scoped invariants. - [ ] Code [Jeff]: Persist resolved invariants + profile in Plan metadata for later Strategize/Execute steps; include invariant source tags. - [ ] Docs [Jeff]: Update `docs/reference/plan_cli.md` with examples for profile + invariants, positional project usage, and error cases. - - [ ] Tests (Behave) [Rui]: Add scenarios in `features/plan_lifecycle_cli.feature` covering profile selection, invariant validation, positional project args, and multiple projects. - - [ ] Tests (Robot) [Rui]: Add Robot tests for plan use with invariants and automation profiles (positive + negative cases). - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_use_cli_bench.py` for argument parsing and validation. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add scenarios in `features/plan_lifecycle_cli.feature` covering profile selection, invariant validation, positional project args, and multiple projects. + - [ ] Tests (Robot) [Jeff]: Add Robot tests for plan use with invariants and automation profiles (positive + negative cases). + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/plan_use_cli_bench.py` for argument parsing and validation. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(cli): extend plan use with invariants and automation profile"`. - [ ] **COMMIT (Owner: Rui | Group: A4b.beta) - Commit message: "test(cli): add plan lifecycle Behave and Robot coverage"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Tests (Behave) [Rui]: Add full coverage for `plan execute`, `plan apply`, `plan status`, `plan list`, `plan cancel` (success + error paths, invalid phase, missing plan, multiple plans ready). - [ ] Tests (Robot) [Rui]: Add `robot/plan_lifecycle_cli.robot` covering end-to-end lifecycle transitions with real DB persistence. - [ ] Docs [Rui]: Update `docs/development/testing.md` with new CLI suites and command mappings. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_cli_smoke_bench.py` for CLI argument parsing overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Rui]: `git commit -m "test(cli): add plan lifecycle Behave and Robot coverage"`. **Parallel Group A5: Plan Persistence (M1-critical)** @@ -1398,92 +1311,122 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **SEQUENTIAL AFTER alpha+beta [Jeff + Luis]**: Repositories + service integration **PARALLEL CONTINUOUS [Rui]**: Persistence tests added inside each commit **SEQUENTIAL NOTE**: `action_arguments` migration must land after `actions` migration; A5.legacy should land after A4b CLI alignment + A5.gamma persistence wiring. + **SEQUENTIAL NOTE**: A5.alpha migrations must match A2b fields; rebase Alembic head after A2b merges before cutting follow-on revisions. - [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add actions and action_invariants tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add Alembic migration for `actions` table with ULID PK, namespaced_name, actor refs (strategy/execution/review/apply/estimation), DoD fields, automation_profile, invariant_actor, reusable/read_only flags, tags_json, created_by, timestamps. - - [ ] Code [Jeff]: Add `action_invariants` table with FK to actions, `scope` column, `invariant_text`, and created_at timestamp. - - [ ] Code [Jeff]: Add unique index on actions.namespaced_name and search index on namespace for list filtering. + - [ ] Code [Jeff]: Add Alembic migration skeleton with explicit down_revision dependency and naming conventions for indexes/constraints. + - [ ] Code [Jeff]: Create `actions` table with ULID PK, `namespaced_name`, `namespace`, `name`, and explicit actor refs (strategy/execution/review/apply/estimation). + - [ ] Code [Jeff]: Add `action_state` enum column (draft/available/archived) with default draft and validation-friendly values. + - [ ] Code [Jeff]: Add description columns (`short_description`, `long_description`) and DoD columns (`definition_of_done`, `definition_of_done_template`). + - [ ] Code [Jeff]: Add behavioral columns (`automation_profile`, `invariant_actor`, `reusable`, `read_only`) and metadata (`tags_json`, `created_by`, timestamps). + - [ ] Code [Jeff]: Add `action_invariants` table with FK to actions, `scope` column, `invariant_text`, optional `position`, and created_at timestamp. + - [ ] Code [Jeff]: Add unique index on `actions.namespaced_name`, index on `actions.namespace`, and index on `actions.action_state` for list filters. - [ ] Code [Jeff]: Ensure downgrade path drops indexes and tables in reverse order. - [ ] Docs [Jeff]: Update `docs/reference/database_schema.md` with column-level details and constraints. - - [ ] Tests (Behave) [Rui]: Add migration scenario that runs upgrade and asserts tables + indexes exist. - - [ ] Tests (Robot) [Rui]: Add Robot migration smoke test using `nox -s db_migrate` (create session if missing). - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_actions_bench.py` for migration runtime baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add migration scenario that runs upgrade and asserts tables + indexes exist. + - [ ] Tests (Robot) [Jeff]: Add Robot migration smoke test using `nox -s db_migrate` (create session if missing). + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/db_migration_actions_bench.py` for migration runtime baseline. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(db): add actions and action_invariants tables"`. - [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add action_arguments table"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add Alembic migration for `action_arguments` table (action_id FK, name, type, requirement, description, default_value_json, min_value, max_value, validation_pattern). - - [ ] Code [Jeff]: Add uniqueness constraint on (action_id, name) and index on action_id. + - [ ] Code [Jeff]: Add Alembic migration for `action_arguments` table with ULID-less FK to actions and ordered `position` for deterministic argument ordering. + - [ ] Code [Jeff]: Add columns for `name`, `arg_type`, `requirement`, `description`, `default_value_json`, `min_value`, `max_value`, `validation_pattern`. + - [ ] Code [Jeff]: Add check constraints for numeric min/max ordering and non-empty argument names. + - [ ] Code [Jeff]: Add uniqueness constraint on (action_id, name) and index on (action_id, position). - [ ] Docs [Jeff]: Update `docs/reference/database_schema.md` with action_arguments columns and constraints. - - [ ] Tests (Behave) [Rui]: Add migration scenario verifying action_arguments table and constraints. - - [ ] Tests (Robot) [Rui]: Add Robot migration smoke test that inserts a row and queries it. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_action_args_bench.py` for migration baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add migration scenario verifying action_arguments table and constraints. + - [ ] Tests (Robot) [Jeff]: Add Robot migration smoke test that inserts a row and queries it. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/db_migration_action_args_bench.py` for migration baseline. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(db): add action_arguments table"`. - [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add lifecycle_plans and plan_projects tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add Alembic migration for `lifecycle_plans` with ULID PK, phase/state enums, action linkage (action_id + action_name), automation_profile, invariant_actor, definition_of_done_template, and timestamps. - - [ ] Code [Jeff]: Add `plan_projects` table with plan_id, project_name (namespaced), read_only flag, and alias. - - [ ] Code [Jeff]: Add indexes on plan phase/state for filtering and plan_projects.project_name for lookups. + - [ ] Code [Jeff]: Add Alembic migration for `lifecycle_plans` with ULID PK and identity fields (parent_plan_id, root_plan_id, attempt). + - [ ] Code [Jeff]: Add core plan columns: `namespaced_name`, `namespace`, `description`, `definition_of_done`, `definition_of_done_template`. + - [ ] Code [Jeff]: Add lifecycle columns: `phase` enum, `processing_state` enum, `action_state` enum (nullable after Action), and timestamps for each phase. + - [ ] Code [Jeff]: Add action linkage columns (`action_id`, `action_name`) and actor refs (strategy/execution/review/apply/estimation). + - [ ] Code [Jeff]: Add policy/metadata columns (`automation_profile`, `invariant_actor`, `read_only`, `reusable`, `created_by`, `tags_json`). + - [ ] Code [Jeff]: Add execution placeholders (`changeset_id`, `sandbox_refs_json`, `validation_summary_json`, `decision_root_id`, `error_message`, `error_details_json`). + - [ ] Code [Jeff]: Add `plan_projects` table with plan_id, project_name (namespaced), alias, read_only flag, and created_at. + - [ ] Code [Jeff]: Add uniqueness constraint on (plan_id, project_name) and index on (project_name) for lookups. + - [ ] Code [Jeff]: Add indexes on `phase`, `processing_state`, and `namespace` for list filtering. - [ ] Docs [Jeff]: Update schema docs with plan/project link rules and uniqueness constraints. - - [ ] Tests (Behave) [Rui]: Add migration scenario verifying plan/project link table + indexes. - - [ ] Tests (Robot) [Rui]: Add Robot test that inserts a plan/project link row and queries it. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_plans_bench.py` for migration runtime baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add migration scenario verifying plan/project link table + indexes. + - [ ] Tests (Robot) [Jeff]: Add Robot test that inserts a plan/project link row and queries it. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/db_migration_plans_bench.py` for migration runtime baseline. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(db): add lifecycle_plans and plan_projects tables"`. - [ ] **COMMIT (Owner: Jeff | Group: A5.alpha) - Commit message: "feat(db): add plan arguments and plan invariants tables"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Add `plan_arguments` table (plan_id, name, value_json, value_type) and `plan_invariants` table (plan_id, invariant_text, source_scope). + - [ ] Code [Jeff]: Add `plan_arguments` table with plan_id, name, value_json, value_type, and `position` for stable ordering. + - [ ] Code [Jeff]: Add `plan_invariants` table with plan_id, invariant_text, source_scope, optional `position`, and created_at. - [ ] Code [Jeff]: Add uniqueness constraint on (plan_id, name) for arguments and (plan_id, invariant_text) for invariants. + - [ ] Code [Jeff]: Add index on (plan_id, position) for fast ordered retrieval. - [ ] Docs [Jeff]: Document argument storage, JSON serialization rules, and invariant source scopes. - - [ ] Tests (Behave) [Rui]: Add migration scenario verifying both tables and constraints. - - [ ] Tests (Robot) [Rui]: Add Robot test that inserts a plan invariant and asserts retrieval. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_migration_plan_args_bench.py` for migration runtime baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add migration scenario verifying both tables and constraints. + - [ ] Tests (Robot) [Jeff]: Add Robot test that inserts a plan invariant and asserts retrieval. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/db_migration_plan_args_bench.py` for migration runtime baseline. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(db): add plan arguments and plan invariants tables"`. - [ ] **COMMIT (Owner: Luis | Group: A5.beta) - Commit message: "feat(models): add action and lifecycle plan ORM models"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Luis]: Add SQLAlchemy models for Action, ActionInvariant, ActionArgument, LifecyclePlan, PlanProjectLink, PlanArgument, PlanInvariant. - - [ ] Code [Luis]: Implement `to_domain()` and `from_domain()` mappings with ULID validation, enum conversion, and timestamp normalization. - - [ ] Code [Luis]: Add repository-facing helpers for filtering by namespace/phase/state and eager-load action arguments. + - [ ] Code [Luis]: Add SQLAlchemy base model mixins for ULID PKs, timestamps, and JSON columns (reused by action/plan models). + - [ ] Code [Luis]: Implement `ActionModel` with columns for namespaced_name, namespace, actor refs, DoD fields, automation_profile, invariant_actor, state, tags_json, created_by. + - [ ] Code [Luis]: Implement `ActionInvariantModel` with FK to actions, scope, invariant_text, position, and created_at. + - [ ] Code [Luis]: Implement `ActionArgumentModel` with FK to actions, name, arg_type, requirement, defaults/min/max/regex, position, and constraints. + - [ ] Code [Luis]: Implement `LifecyclePlanModel` with identity fields, phase/state/processing enums, action linkage, DoD fields, policy metadata, and execution placeholders. + - [ ] Code [Luis]: Implement `PlanProjectLinkModel` with plan_id, project_name, alias, read_only, and created_at, plus uniqueness constraint. + - [ ] Code [Luis]: Implement `PlanArgumentModel` and `PlanInvariantModel` with ordered `position` fields and constraints. + - [ ] Code [Luis]: Define ORM relationships with ordering (`order_by=position`) and cascade rules for argument/invariant collections. + - [ ] Code [Luis]: Implement `to_domain()` and `from_domain()` mappers for each model with ULID validation, enum conversion, and timestamp normalization. + - [ ] Code [Luis]: Add repository-facing helpers for filtering by namespace/phase/state and eager-load action arguments + plan links. - [ ] Docs [Luis]: Update ORM mapping notes in `docs/reference/database_schema.md` with model field mapping table. - - [ ] Tests (Behave) [Rui]: Add scenarios for ORM round-trip serialization and enum conversions. - - [ ] Tests (Robot) [Rui]: Add Robot test that loads a plan and asserts field mapping correctness. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/orm_mapping_bench.py` for Action/Plan mapping throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add scenarios for ORM round-trip serialization, enum conversions, and ordered argument persistence. + - [ ] Tests (Robot) [Luis]: Add Robot test that loads a plan and asserts field mapping correctness. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/orm_mapping_bench.py` for Action/Plan mapping throughput. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(models): add action and lifecycle plan ORM models"`. - [ ] **COMMIT (Owner: Jeff | Group: A5.gamma) - Commit message: "feat(repo): add action and lifecycle plan repositories"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - - [ ] Code [Jeff]: Implement ActionRepository CRUD + list filters by namespace/state/automation profile; include ActionArgument persistence. - - [ ] Code [Jeff]: Implement PlanRepository CRUD + list filters by phase/state/project; add plan lookup by namespaced name. + - [ ] Code [Jeff]: Define repository interfaces in `src/cleveragents/domain/repositories/` for ActionRepository and PlanRepository (methods + expected errors). + - [ ] Code [Jeff]: Implement ActionRepository CRUD with deterministic ordering (created_at) and filters (namespace, state, automation_profile). + - [ ] Code [Jeff]: Implement ActionRepository persistence for arguments + invariants with ordered `position` preservation. + - [ ] Code [Jeff]: Implement PlanRepository CRUD with filters (phase/state/project_name/action_name) and lookup by namespaced_name. + - [ ] Code [Jeff]: Implement PlanRepository persistence for plan_projects, plan_arguments, plan_invariants with ordered retrieval. - [ ] Code [Jeff]: Add retry decorator to repositories; retry only on `OperationalError`, never on `IntegrityError`. + - [ ] Code [Jeff]: Add pagination parameters (`limit`, `offset`) with default ordering for list queries. - [ ] Docs [Jeff]: Document repository interfaces, error types, and pagination guidance. - - [ ] Tests (Behave) [Rui]: Add scenarios for repository create/get/list/update/delete guardrails, including action argument round-trips. - - [ ] Tests (Robot) [Rui]: Add Robot test that exercises repository through service layer. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/repository_query_bench.py` for list + filter performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add scenarios for repository create/get/list/update/delete guardrails, including action argument round-trips. + - [ ] Tests (Robot) [Jeff]: Add Robot test that exercises repository through service layer. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/repository_query_bench.py` for list + filter performance. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(repo): add action and lifecycle plan repositories"`. - [ ] **COMMIT (Owner: Luis | Group: A5.gamma) - Commit message: "feat(service): persist plan lifecycle via repositories"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Luis]: Update `PlanLifecycleService` to use repositories instead of in-memory dicts. - - [ ] Code [Luis]: Ensure plan creation persists arguments, invariants, automation profile, action linkage, and project links in a single transaction. - - [ ] Code [Luis]: Add transactional safeguards for multi-step updates (create action + plan, correction updates) and roll back on errors. + - [ ] Code [Luis]: Replace in-memory action/plan maps with repository lookups in `get_action`, `get_plan`, and list helpers. + - [ ] Code [Luis]: Persist action creation with arguments/invariants and enforce namespaced_name uniqueness. + - [ ] Code [Luis]: Persist plan creation with project link metadata (alias/read_only) and store plan_arguments/plan_invariants in same transaction. + - [ ] Code [Luis]: Wrap transitions in UnitOfWork transactions and map DB errors to domain errors (duplicate names, missing action). + - [ ] Code [Luis]: Add optimistic guards for phase transitions (validate phase/state before update; reload on conflict). - [ ] Docs [Luis]: Update service docs to reflect persistence and remove in-memory notes. - - [ ] Tests (Behave) [Rui]: Add scenarios for persisted lifecycle transitions and error handling (duplicate names, invalid transitions). - - [ ] Tests (Robot) [Rui]: Add end-to-end test that restarts the app and re-reads plan state. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_lifecycle_service_bench.py` for persistence operations. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add scenarios for persisted lifecycle transitions and error handling (duplicate names, invalid transitions). + - [ ] Tests (Robot) [Luis]: Add end-to-end test that restarts the app and re-reads plan state. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/plan_lifecycle_service_bench.py` for persistence operations. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(service): persist plan lifecycle via repositories"`. - [ ] **COMMIT (Owner: Luis | Group: A5.gamma) - Commit message: "feat(di): wire lifecycle repos and services"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Luis]: Register ActionRepository + PlanRepository in `application/container.py` and UnitOfWork. - - [ ] Code [Luis]: Inject repositories into PlanLifecycleService and CLI commands; remove direct service instantiation in CLI. - - [ ] Code [Luis]: Add container wiring tests to ensure singleton lifetimes are correct. + - [ ] Code [Luis]: Register PlanLifecycleService with repository dependencies and settings in container. + - [ ] Code [Luis]: Inject lifecycle service into CLI commands; remove direct service instantiation in `action.py` and `plan.py`. + - [ ] Code [Luis]: Add container wiring tests to ensure singleton lifetimes are correct and repositories share UoW session. - [ ] Docs [Luis]: Update DI wiring notes in `docs/architecture/decisions/adr-003.md`. - - [ ] Tests (Behave) [Rui]: Add scenarios that use container wiring for lifecycle commands. - - [ ] Tests (Robot) [Rui]: Add Robot smoke test verifying CLI uses persisted service. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/di_container_bench.py` for container resolution overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add scenarios that use container wiring for lifecycle commands. + - [ ] Tests (Robot) [Luis]: Add Robot smoke test verifying CLI uses persisted service. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/di_container_bench.py` for container resolution overhead. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(di): wire lifecycle repos and services"`. - [ ] **COMMIT (Owner: Rui | Group: A5.tests) - Commit message: "test(persistence): add plan/action persistence suites"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) @@ -1492,8 +1435,8 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Tests (Robot) [Rui]: Add plan persistence E2E (full lifecycle, restart persistence, concurrent CLI access). - [ ] Docs [Rui]: Update `docs/development/testing.md` with persistence suites and `nox` commands. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/persistence_suites_bench.py` for DB read/write baselines. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Rui]: `git commit -m "test(persistence): add plan/action persistence suites"`. **Parallel Group A5.legacy: Remove legacy plan build/apply path (M1-critical)** @@ -1504,11 +1447,11 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Jeff]: Rename `plan lifecycle-apply` to `plan apply` and update command wiring once legacy apply is removed. - [ ] Code [Jeff]: Remove or archive legacy `PlanModel`/`PlanStatus` DB tables if unused by v3; document migration path. - [ ] Docs [Jeff]: Update CLI docs to list only v3 lifecycle commands and new `plan use/execute/apply` flows. - - [ ] Tests (Behave) [Rui]: Remove/replace legacy scenarios with v3 equivalents and adjust coverage expectations. - - [ ] Tests (Robot) [Rui]: Remove legacy robot suites and add v3 replacements where needed. - - [ ] Tests (ASV) [Rui]: Update asv suite to remove legacy plan benchmarks and add v3 lifecycle baseline benchmark. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Remove/replace legacy scenarios with v3 equivalents and adjust coverage expectations. + - [ ] Tests (Robot) [Jeff]: Remove legacy robot suites and add v3 replacements where needed. + - [ ] Tests (ASV) [Jeff]: Update asv suite to remove legacy plan benchmarks and add v3 lifecycle baseline benchmark. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "refactor(plan): remove legacy plan service and CLI"`. **Parallel Group A6: Automation Profiles Foundation [Jeff + Luis]** (M1-critical; depends on A5 persistence) @@ -1521,22 +1464,22 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Jeff]: Add built-in profiles (`manual`, `review`, `supervised`, `full-auto`, etc.) per spec with constant definitions and stable IDs. - [ ] Code [Jeff]: Add YAML schema for automation profiles under `docs/schema/automation_profile.schema.yaml` and loader helper. - [ ] Docs [Jeff]: Add `docs/reference/automation_profiles.md` describing built-ins and threshold semantics. - - [ ] Tests (Behave) [Rui]: Add scenarios for profile validation and built-in defaults. - - [ ] Tests (Robot) [Rui]: Add Robot test that loads each built-in profile and prints summary. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_bench.py` for profile validation. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add scenarios for profile validation and built-in defaults. + - [ ] Tests (Robot) [Jeff]: Add Robot test that loads each built-in profile and prints summary. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/automation_profile_bench.py` for profile validation. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(domain): add automation profile model and built-ins"`. - [ ] **COMMIT (Owner: Luis | Group: A6.service) - Commit message: "feat(service): resolve automation profiles with precedence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Luis]: Add `AutomationProfileService` to resolve profiles with precedence (plan > action > project > global). - [ ] Code [Luis]: Add persistence table `automation_profiles` (namespaced name PK) and repository with list/show/update. - [ ] Code [Luis]: Add config key `core.automation_profile` and env var override for global default. - [ ] Docs [Luis]: Update `docs/reference/config.md` with automation profile defaults and override behavior. - - [ ] Tests (Behave) [Rui]: Add scenarios for precedence resolution and missing profile errors. - - [ ] Tests (Robot) [Rui]: Add Robot config smoke test for global profile override. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_resolution_bench.py` for resolution latency. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add scenarios for precedence resolution and missing profile errors. + - [ ] Tests (Robot) [Luis]: Add Robot config smoke test for global profile override. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/automation_profile_resolution_bench.py` for resolution latency. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(service): resolve automation profiles with precedence"`. - [ ] **COMMIT (Owner: Rui | Group: A6.cli) - Commit message: "feat(cli): add automation-profile commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Rui]: Implement `agents automation-profile add/remove/list/show` commands with YAML config input. @@ -1545,8 +1488,8 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Tests (Behave) [Rui]: Add CLI scenarios for profile add/list/show/remove. - [ ] Tests (Robot) [Rui]: Add Robot CLI tests for automation-profile commands. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/automation_profile_cli_bench.py` for CLI parsing. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Rui]: `git commit -m "feat(cli): add automation-profile commands"`. **M1 SUCCESS CRITERIA (Day 7 MVP - source code only)**: @@ -1561,8 +1504,10 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target ### Section 4: Projects & Resources [WORKSTREAM B - Hamza Lead] **Target: Milestone M2 (+10 days)** +**Week 1-2 focus**: local source code only (git-checkout + fs-directory). Database, API, and remote resources are schema-only stubs for future work. **Parallel Group B1: Resource Registry Core [Hamza + Jeff]** (can start after A5.alpha migrations are available) + **SEQUENTIAL NOTE**: B1 domain models can start immediately; B1 DB migrations must rebase on the latest Alembic head after A5.alpha to keep a linear migration chain. - [ ] **COMMIT (Owner: Hamza | Group: B1.core) - Commit message: "feat(domain): add resource type spec and resource model"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Create `src/cleveragents/domain/models/core/resource_type.py` with `ResourceTypeSpec`, `ResourceTypeArgument`, `ResourceKind` (physical/virtual), and `SandboxStrategy` enum. - [ ] Code [Hamza]: Add resource type fields: `user_addable`, `allowed_parents`, `allowed_children`, `auto_discover`, `handler` reference, and `sandbox_strategy` default. @@ -1571,91 +1516,106 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Hamza]: Add `docs/schema/resource_type.schema.yaml` with CLI argument definitions, parent/child constraints, and handler metadata. - [ ] Code [Hamza]: Add resource type YAML loader in `src/cleveragents/resource/schema.py` with version guard and clear error messages. - [ ] Docs [Hamza]: Add `docs/reference/resource_model.md` with examples for git-checkout and fs-directory resources plus physical/virtual notes. - - [ ] Tests (Behave) [Rui]: Add scenarios validating ULID format, namespace rules, allowed parent/child type checks, and sandbox strategy defaults. - - [ ] Tests (Robot) [Rui]: Add Robot test that loads a ResourceTypeSpec YAML fixture and validates it. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_model_bench.py` for resource validation + DAG checks. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add scenarios validating ULID format, namespace rules, allowed parent/child type checks, and sandbox strategy defaults. + - [ ] Tests (Robot) [Hamza]: Add Robot test that loads a ResourceTypeSpec YAML fixture and validates it. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/resource_model_bench.py` for resource validation + DAG checks. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(domain): add resource type spec and resource model"`. +- [ ] **COMMIT (Owner: Hamza | Group: B1.core) - Commit message: "feat(resource): add built-in resource type configs"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Hamza]: Add built-in resource type YAML configs under `resources/types/` (git-checkout, fs-directory, fs-file) with sandbox strategy defaults and CLI argument specs. + - [ ] Code [Hamza]: Add bootstrap registration in `ResourceRegistryService` (register built-ins on startup if missing; idempotent). + - [ ] Code [Hamza]: Add mapping table from built-in type to handler/sandbox strategy and surface in `resource type list` output. + - [ ] Docs [Hamza]: Add `docs/reference/resource_types_builtin.md` with per-type flags and examples. + - [ ] Tests (Behave) [Hamza]: Add scenarios ensuring built-in types exist and register idempotently. + - [ ] Tests (Robot) [Hamza]: Add Robot test that lists resource types and asserts built-ins are present. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/resource_type_bootstrap_bench.py` for registration overhead. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(resource): add built-in resource type configs"`. - [ ] **COMMIT (Owner: Hamza | Group: B1.core) - Commit message: "feat(domain): add project model v3 with linked resources"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Add `Project`, `ProjectResourceLink`, `ProjectValidationSummary` (derived from validation attachments), and `ProjectContextPolicy` models using namespaced name as the unique identifier (no ULID per spec). - [ ] Code [Hamza]: Add fields for `invariants`, `invariant_actor`, `automation_profile`, and `context_views` (strategize/execute/apply/default). - [ ] Code [Hamza]: Add validation for resource link overrides (read_only flags, alias uniqueness, resource existence). - [ ] Code [Hamza]: Add helpers to compute effective invariants and automation profile (project defaults). - [ ] Docs [Hamza]: Add `docs/reference/project_model.md` describing resource linking, validation attachments, and context view policies. - - [ ] Tests (Behave) [Rui]: Add scenarios for project model validation, link overrides, and context view inheritance. - - [ ] Tests (Robot) [Rui]: Add Robot test that creates a Project object and prints serialized output. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_model_bench.py` for serialization/validation performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add scenarios for project model validation, link overrides, and context view inheritance. + - [ ] Tests (Robot) [Hamza]: Add Robot test that creates a Project object and prints serialized output. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/project_model_bench.py` for serialization/validation performance. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(domain): add project model v3 with linked resources"`. - [ ] **COMMIT (Owner: Jeff | Group: B1.core) - Commit message: "feat(db): add resource registry tables"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Add Alembic migration for `resource_types`, `resources`, and `resource_edges` tables with indexes on type/name/namespace. - - [ ] Code [Jeff]: Store `resource_kind` (physical/virtual), `sandbox_strategy`, and optional `namespaced_name` in `resources`. - - [ ] Code [Jeff]: Add foreign keys and cascade rules for resource_edges (parent/child) with uniqueness constraint. + - [ ] Code [Jeff]: Add Alembic migration for `resource_types`, `resources`, and `resource_edges` tables with naming conventions. + - [ ] Code [Jeff]: Define `resource_types` columns: `name`, `namespace`, `description`, `resource_kind`, `sandbox_strategy`, `user_addable`, `handler_ref`, `args_schema_json`, `allowed_parent_types_json`, `allowed_child_types_json`, `auto_discover_json`, timestamps. + - [ ] Code [Jeff]: Define `resources` columns: ULID PK, `namespaced_name`, `namespace`, `type_name`, `resource_kind`, `location`, `description`, `read_only`, `metadata_json`, `sandbox_strategy`, timestamps. + - [ ] Code [Jeff]: Define `resource_edges` columns: `parent_id`, `child_id`, `created_at`, with uniqueness constraint and FK cascade rules. + - [ ] Code [Jeff]: Add indexes on `resources.namespaced_name`, `resources.namespace`, `resources.type_name`, and `resource_edges.parent_id/child_id`. - [ ] Docs [Jeff]: Update `docs/reference/database_schema.md` with resource registry tables and constraints. - - [ ] Tests (Behave) [Rui]: Add migration scenarios verifying tables, indices, and edge uniqueness. - - [ ] Tests (Robot) [Rui]: Add Robot migration smoke test using `nox -s db_migrate`. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_registry_migration_bench.py` for migration baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add migration scenarios verifying tables, indices, and edge uniqueness. + - [ ] Tests (Robot) [Jeff]: Add Robot migration smoke test using `nox -s db_migrate`. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/resource_registry_migration_bench.py` for migration baseline. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(db): add resource registry tables"`. **Parallel Group B2: Project Persistence + Services [Hamza + Luis]** (depends on B1 domain models) - [ ] **COMMIT (Owner: Jeff | Group: B2.persistence) - Commit message: "feat(db): add projects and project links tables"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Jeff]: Add Alembic migration for `projects` and `project_resource_links` tables (no standalone project_validations table). - - [ ] Code [Jeff]: Store `automation_profile`, `invariant_actor`, `invariants_json`, and `context_policy_json` on `projects` table. - - [ ] Code [Jeff]: Use namespaced name as project primary key; enforce unique constraint on `projects.namespaced_name`. - - [ ] Code [Jeff]: Add indexes for `project_resource_links.project_name` and `resource_id` for fast joins. + - [ ] Code [Jeff]: Add Alembic migration skeleton with explicit down_revision to latest A5.alpha head. + - [ ] Code [Jeff]: Define `projects` table with namespaced_name PK, namespace, description, automation_profile, invariant_actor, invariants_json, context_policy_json, tags_json, created_by, timestamps. + - [ ] Code [Jeff]: Add `projects` constraints for non-empty names, namespace/name derivation consistency, and unique namespaced_name. + - [ ] Code [Jeff]: Define `project_resource_links` table with link_id ULID, project_name FK, resource_id FK, alias, read_only, created_at. + - [ ] Code [Jeff]: Add uniqueness constraint on (project_name, resource_id) and index on (project_name, alias) for fast lookups. + - [ ] Code [Jeff]: Add indexes on `project_resource_links.project_name` and `project_resource_links.resource_id` for joins. - [ ] Docs [Jeff]: Document project table schema and link semantics in `docs/reference/database_schema.md`. - - [ ] Tests (Behave) [Rui]: Add migration scenarios verifying project tables and constraints. - - [ ] Tests (Robot) [Rui]: Add Robot test that inserts a project and link row. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_migration_bench.py` for migration baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add migration scenarios verifying project tables, FK constraints, and unique link enforcement. + - [ ] Tests (Robot) [Jeff]: Add Robot test that inserts a project and link row and validates alias uniqueness. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/project_migration_bench.py` for migration baseline. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(db): add projects and project links tables"`. - [ ] **COMMIT (Owner: Hamza | Group: B2.persistence) - Commit message: "feat(repo): add resource repositories"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Implement `ResourceTypeRepository` CRUD and `ResourceRepository` CRUD with DAG edge helpers. - [ ] Code [Hamza]: Add methods for tree traversal, child discovery queries, and name/ULID resolution. - [ ] Code [Hamza]: Add repository guardrails for preventing cycles and duplicate edges. - [ ] Docs [Hamza]: Document repository interfaces in `docs/reference/repositories.md`. - - [ ] Tests (Behave) [Rui]: Add repository scenarios for create/get/list/tree and cycle rejection. - - [ ] Tests (Robot) [Rui]: Add Robot test exercising tree output ordering. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_repository_bench.py` for tree query performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add repository scenarios for create/get/list/tree and cycle rejection. + - [ ] Tests (Robot) [Hamza]: Add Robot test exercising tree output ordering. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/resource_repository_bench.py` for tree query performance. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(repo): add resource repositories"`. - [ ] **COMMIT (Owner: Hamza | Group: B2.persistence) - Commit message: "feat(repo): add project repositories"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Implement `ProjectRepository` and `ProjectResourceLinkRepository` with namespace filtering and name-based lookup. - [ ] Code [Hamza]: Add methods to list project context policies and derived validation attachment summaries for linked resources. - [ ] Docs [Hamza]: Update repository docs with project link examples and validation attachment notes. - - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/unlink and validation attachment summaries. - - [ ] Tests (Robot) [Rui]: Add Robot test that links two resources to one project. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_repository_bench.py` for link/unlink performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add scenarios for project create/link/unlink and validation attachment summaries. + - [ ] Tests (Robot) [Hamza]: Add Robot test that links two resources to one project. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/project_repository_bench.py` for link/unlink performance. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(repo): add project repositories"`. - [ ] **COMMIT (Owner: Hamza | Group: B2.service) - Commit message: "feat(service): add resource registry service"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Implement `ResourceRegistryService` for register/remove/show/tree operations with name/ULID resolution. - [ ] Code [Hamza]: Add auto-discovery hook that delegates to resource handlers (git-checkout for MVP). - [ ] Code [Hamza]: Add validation that resource type supports parent/child linkage before linking. - [ ] Docs [Hamza]: Add `docs/reference/resource_registry.md` describing API behavior and error cases. - - [ ] Tests (Behave) [Rui]: Add scenarios for register/remove/show/tree behavior and auto-discovery. - - [ ] Tests (Robot) [Rui]: Add Robot test that registers a git-checkout and inspects child count. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_registry_service_bench.py` for register/show performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add scenarios for register/remove/show/tree behavior and auto-discovery. + - [ ] Tests (Robot) [Hamza]: Add Robot test that registers a git-checkout and inspects child count. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/resource_registry_service_bench.py` for register/show performance. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(service): add resource registry service"`. - [ ] **COMMIT (Owner: Luis | Group: B2.service) - Commit message: "feat(service): add project service v3"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Implement `ProjectService` create/list/show/delete/link/unlink methods using repositories. - [ ] Code [Luis]: Add validation attachment helpers (read-only listing of validation attachments for linked resources) and context policy setters for project views. - [ ] Code [Luis]: Enforce read-only resource links and project-level invariant actor defaults. - [ ] Docs [Luis]: Update `docs/reference/project_service.md` with usage examples and error cases. - - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/unlink/context policy + validation attachment visibility. - - [ ] Tests (Robot) [Rui]: Add Robot test that creates project and links a resource. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_service_bench.py` for link/unlink performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add scenarios for project create/link/unlink/context policy + validation attachment visibility. + - [ ] Tests (Robot) [Luis]: Add Robot test that creates project and links a resource. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/project_service_bench.py` for link/unlink performance. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(service): add project service v3"`. **Parallel Group B3: CLI Commands [Rui]** (depends on B2 services) @@ -1667,8 +1627,8 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Tests (Behave) [Rui]: Add scenarios for resource type lifecycle and invalid schema handling. - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/resource_type_cli.robot`. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_type_cli_bench.py` for config parsing overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Rui]: `git commit -m "feat(cli): add resource type commands"`. - [ ] **COMMIT (Owner: Rui | Group: B3.cli) - Commit message: "feat(cli): add resource commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Rui]: Add `agents resource add/remove/list/show/tree` commands with type-specific flags and name/ULID resolution. @@ -1678,8 +1638,8 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Tests (Behave) [Rui]: Add scenarios for resource registration, list filters, tree rendering, and link-child constraints. - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/resource_cli.robot`. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/resource_cli_bench.py` for command parsing and list output. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Rui]: `git commit -m "feat(cli): add resource commands"`. - [ ] **COMMIT (Owner: Rui | Group: B3.cli) - Commit message: "feat(cli): add project commands"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Rui]: Add `agents project create/show/list/delete/link-resource/unlink-resource` commands using namespaced project names. @@ -1688,8 +1648,8 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Tests (Behave) [Rui]: Add scenarios for project create/link/unlink/context policies and validation display. - [ ] Tests (Robot) [Rui]: Add Robot suite `robot/project_cli.robot`. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_cli_bench.py` for command parsing and list output. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Rui]: `git commit -m "feat(cli): add project commands"`. **Parallel Group B3.cleanup: Legacy Project Removal [Jeff]** (after B3.cli lands) @@ -1700,11 +1660,11 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Jeff]: Update `src/cleveragents/application/container.py` to stop wiring legacy ProjectService once v3 service is in place. - [ ] Code [Jeff]: Remove legacy `src/cleveragents/domain/models/core/project.py` in favor of v3 project model and update imports. - [ ] Docs [Jeff]: Remove references to `agents project init` from CLI docs and point to `agents project create` + `agents init` (global) flows. - - [ ] Tests (Behave) [Rui]: Remove/replace legacy project init scenarios with v3 project create scenarios. - - [ ] Tests (Robot) [Rui]: Remove legacy project init Robot suites and add v3 replacements if missing. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/project_cli_cleanup_bench.py` for CLI help/rendering baseline after removal. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Remove/replace legacy project init scenarios with v3 project create scenarios. + - [ ] Tests (Robot) [Jeff]: Remove legacy project init Robot suites and add v3 replacements if missing. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/project_cli_cleanup_bench.py` for CLI help/rendering baseline after removal. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "refactor(project): remove legacy project init/status commands"`. **Parallel Group B4: Sandboxing [Luis + Jeff]** (depends on resource registry + project links) @@ -1713,43 +1673,43 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Luis]: Implement lazy sandbox creation, cleanup hooks, and plan-scoped retention policy stubs. - [ ] Code [Luis]: Add sandbox path rewriting helper for tool execution and MCP adapters. - [ ] Docs [Luis]: Add `docs/reference/sandbox.md` describing lifecycle, APIs, and path rewriting rules. - - [ ] Tests (Behave) [Rui]: Add scenarios for sandbox manager creation, cleanup, and path rewrite behavior. - - [ ] Tests (Robot) [Rui]: Add Robot test that creates a sandbox and verifies filesystem isolation. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/sandbox_manager_bench.py` for sandbox creation overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add scenarios for sandbox manager creation, cleanup, and path rewrite behavior. + - [ ] Tests (Robot) [Luis]: Add Robot test that creates a sandbox and verifies filesystem isolation. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/sandbox_manager_bench.py` for sandbox creation overhead. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(sandbox): add sandbox strategy interface and manager"`. - [ ] **COMMIT (Owner: Luis | Group: B4.sandbox) - Commit message: "feat(sandbox): implement git_worktree strategy"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Implement git worktree creation, checkout, and cleanup for git-checkout resources. - [ ] Code [Luis]: Add safe fallback for repositories without clean worktrees and clear error messages. - [ ] Code [Luis]: Record sandbox metadata (worktree path, branch, base commit) for rollback. - [ ] Docs [Luis]: Update sandbox doc with git_worktree usage and rollback behavior. - - [ ] Tests (Behave) [Rui]: Add scenarios for git worktree sandbox creation and rollback. - - [ ] Tests (Robot) [Rui]: Add Robot test that modifies sandbox and verifies original repo unchanged. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/git_worktree_bench.py` for sandbox creation time. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add scenarios for git worktree sandbox creation and rollback. + - [ ] Tests (Robot) [Luis]: Add Robot test that modifies sandbox and verifies original repo unchanged. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/git_worktree_bench.py` for sandbox creation time. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(sandbox): implement git_worktree strategy"`. - [ ] **COMMIT (Owner: Hamza | Group: B4.sandbox) - Commit message: "feat(resource): add git-checkout handler and discovery"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Add git-checkout handler that validates repo path, branch, and read_only flags. - [ ] Code [Hamza]: Implement child resource discovery for fs-directory children (schema-only for now) and record ULID-only children. - [ ] Code [Hamza]: Add sandbox strategy mapping for git-checkout and path normalization helpers. - [ ] Docs [Hamza]: Document git-checkout handler behavior in `docs/reference/resources_git.md`. - - [ ] Tests (Behave) [Rui]: Add scenarios for handler validation and discovery counts. - - [ ] Tests (Robot) [Rui]: Add Robot test registering a git repo and asserting discovered children. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/git_discovery_bench.py` for discovery cost. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add scenarios for handler validation and discovery counts. + - [ ] Tests (Robot) [Hamza]: Add Robot test registering a git repo and asserting discovered children. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/git_discovery_bench.py` for discovery cost. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(resource): add git-checkout handler and discovery"`. - [ ] **COMMIT (Owner: Luis | Group: B4.sandbox) - Commit message: "feat(sandbox): add copy_on_write strategy stub"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Add copy_on_write strategy skeleton with TODOs for large-project optimization. - [ ] Code [Luis]: Raise explicit NotImplementedError with guidance on when it will be available. - [ ] Docs [Luis]: Document that copy_on_write is stubbed for post-M1 work. - - [ ] Tests (Behave) [Rui]: Add scenario that selecting copy_on_write raises NotImplementedError with clear message. - - [ ] Tests (Robot) [Rui]: Add Robot test verifying stub error output. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/sandbox_stub_bench.py` (baseline no-op). - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add scenario that selecting copy_on_write raises NotImplementedError with clear message. + - [ ] Tests (Robot) [Luis]: Add Robot test verifying stub error output. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/sandbox_stub_bench.py` (baseline no-op). + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(sandbox): add copy_on_write strategy stub"`. **M2 MERGE GATE**: @@ -1771,46 +1731,52 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target **Target: Milestone M3 (+14 days)** -**WEEK 2 - CRITICAL FOR MVP** +**Week 2 focus**: Actor YAML, compilation, skills, and tool-based change tracking. **Parallel Group C0: Tool Registry + Validation System [Jeff + Luis]** (start Day 5; precedes C1/C3) **PARALLEL SUBTRACK C0.domain [Jeff]**: Tool + Validation domain models + schemas **PARALLEL SUBTRACK C0.registry [Luis]**: Tool registry persistence + repositories **PARALLEL SUBTRACK C0.cli [Rui]**: CLI commands for tools/validations - **SEQUENTIAL MERGE NOTE**: C0.domain must land before C0.registry/cli; C0.registry before C3 context wiring. + **SEQUENTIAL MERGE NOTE**: C0.domain must land before C0.registry/cli; C0.registry before C3 context wiring. C0.registry migrations must rebase after A5.alpha; C0.binding should wait for B1.core resource type constraints to validate bindings. - [ ] **COMMIT (Owner: Jeff | Group: C0.domain) - Commit message: "feat(tool): add tool and validation domain models"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Jeff]: Add `Tool` model in `src/cleveragents/domain/models/core/tool.py` with namespaced name, description, source type, input/output JSON schema, and capability metadata (read/write/checkpointable). - [ ] Code [Jeff]: Add `ResourceBinding` model with slot definitions and binding modes (context, static, parameter), plus required/optional flags. - [ ] Code [Jeff]: Add `Validation` model as Tool subtype with `mode`, `wraps`, and `transform` fields; enforce read-only constraints. - [ ] Code [Jeff]: Add enums for ToolSource, ToolType (tool/validation), and ValidationMode. + - [ ] Code [Jeff]: Add `docs/schema/tool.schema.yaml` and `docs/schema/validation.schema.yaml` with required fields, `wraps`/`transform` rules, and resource binding definitions. + - [ ] Code [Jeff]: Add YAML loader in `src/cleveragents/tool/schema.py` that validates schema version, normalizes keys, and returns Tool/Validation domain models. + - [ ] Code [Jeff]: Add example configs under `examples/tools/` and `examples/validations/` (plain tool, validation, wrapped validation) for tests. - [ ] Docs [Jeff]: Add `docs/reference/tool_model.md` and `docs/reference/validation_model.md` with examples. - - [ ] Tests (Behave) [Rui]: Add `features/tool_model.feature` for schema validation, resource binding rules, and validation constraints. - - [ ] Tests (Robot) [Rui]: Add `robot/tool_model.robot` smoke tests for model creation. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_model_bench.py` for schema validation throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/tool_model.feature` for schema validation, resource binding rules, validation constraints, and YAML loader errors. + - [ ] Tests (Robot) [Jeff]: Add `robot/tool_model.robot` smoke tests for model creation. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/tool_model_bench.py` for schema validation throughput (model + YAML loader). + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(tool): add tool and validation domain models"`. - [ ] **COMMIT (Owner: Luis | Group: C0.registry) - Commit message: "feat(tool): add tool registry persistence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Luis]: Add DB tables for `tools`, `tool_bindings`, and `validation_attachments` with indexes on namespaced name and type. - - [ ] Code [Luis]: Include validation attachment columns for resource_id, optional project/plan scope, args_json, and attachment_id ULID. - - [ ] Code [Luis]: Implement ToolRepository + ValidationAttachmentRepository with list/show filters. - - [ ] Code [Luis]: Add ToolRegistryService for register/update/remove/list/show with name conflict checks. + - [ ] Code [Luis]: Define `tools` columns: `namespaced_name`, `namespace`, `tool_type`, `source`, `description`, `input_schema_json`, `output_schema_json`, `capability_json`, `metadata_json`, `yaml_text`, timestamps. + - [ ] Code [Luis]: Define `tool_bindings` columns: `binding_id` ULID, `tool_name`, `slot_name`, `binding_mode`, `resource_type`, `required`, `static_resource_id`, `static_resource_name`, timestamps. + - [ ] Code [Luis]: Define `validation_attachments` columns: `attachment_id` ULID, `validation_name`, `resource_id`, optional `project_name`, optional `plan_id`, `args_json`, timestamps. + - [ ] Code [Luis]: Add uniqueness constraints for `tools.namespaced_name` and `tool_bindings(tool_name, slot_name)`; index `validation_attachments.resource_id`. + - [ ] Code [Luis]: Implement ToolRepository + ValidationAttachmentRepository with list/show filters and eager-loading of bindings. + - [ ] Code [Luis]: Add ToolRegistryService for register/update/remove/list/show with name conflict checks and tool/validation type enforcement. - [ ] Docs [Luis]: Update `docs/reference/database_schema.md` with tool/validation tables. - - [ ] Tests (Behave) [Rui]: Add `features/tool_registry.feature` for register/update/remove and validation-only constraints. - - [ ] Tests (Robot) [Rui]: Add `robot/tool_registry.robot` for list/show smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_registry_bench.py` for registry list performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add `features/tool_registry.feature` for register/update/remove and validation-only constraints. + - [ ] Tests (Robot) [Luis]: Add `robot/tool_registry.robot` for list/show smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/tool_registry_bench.py` for registry list performance. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(tool): add tool registry persistence"`. - [ ] **COMMIT (Owner: Jeff | Group: C0.binding) - Commit message: "feat(tool): add resource binding resolution"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Jeff]: Implement binding resolution for contextual, static, and parameter bindings with type compatibility checks. - [ ] Code [Jeff]: Add resolution helpers for resource name/ULID lookup and project-scoped filtering. - [ ] Docs [Jeff]: Add `docs/reference/tool_bindings.md` with resolution order and examples. - - [ ] Tests (Behave) [Rui]: Add binding resolution scenarios (context vs static vs parameter). - - [ ] Tests (Robot) [Rui]: Add Robot test resolving a bound resource by name. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/binding_resolution_bench.py` for resolution latency. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add binding resolution scenarios (context vs static vs parameter). + - [ ] Tests (Robot) [Jeff]: Add Robot test resolving a bound resource by name. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/binding_resolution_bench.py` for resolution latency. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(tool): add resource binding resolution"`. - [ ] **COMMIT (Owner: Rui | Group: C0.cli) - Commit message: "feat(cli): add tool and validation commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Rui]: Implement `agents tool add/remove/list/show` with YAML config input and `--type` filter. @@ -1820,30 +1786,94 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Tests (Behave) [Rui]: Add CLI scenarios for tool/validation registration and attachment. - [ ] Tests (Robot) [Rui]: Add Robot CLI suites for tool and validation commands. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_cli_bench.py` for CLI parsing overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Rui]: `git commit -m "feat(cli): add tool and validation commands"`. +**Parallel Group C0.skill: Skill Registry & YAML [Aditya + Jeff + Luis + Rui]** (depends on C0.domain + C0.registry; must land before C3.protocol) + **PARALLEL SUBTRACK C0.skill.schema [Aditya]**: Skill YAML schema + examples + **PARALLEL SUBTRACK C0.skill.domain [Jeff]**: Skill domain model + resolver + **PARALLEL SUBTRACK C0.skill.registry [Luis]**: Skill persistence + service + **PARALLEL SUBTRACK C0.skill.cli [Rui]**: CLI commands + output formatting + **SEQUENTIAL MERGE NOTE**: C0.skill.domain must land before C0.skill.registry/C0.skill.cli to avoid dual representations. +- [ ] **COMMIT (Owner: Aditya | Group: C0.skill.schema) - Commit message: "docs(skill): add skill yaml schema and examples"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Docs [Aditya]: Author `docs/schema/skill.schema.yaml` with versioning, required fields, and explicit type constraints for tool refs, inline tools, includes, and MCP sources. + - [ ] Docs [Aditya]: Add skill YAML examples under `examples/skills/` (single-tool, composed, inline tool, validation-only, MCP-backed). + - [ ] Code [Aditya]: Add schema loader in `src/cleveragents/skills/schema.py` that validates schema version, normalizes keys, and returns typed data. + - [ ] Code [Aditya]: Add clear validation errors for missing tools, recursive includes, and invalid namespaced names. + - [ ] Tests (Behave) [Aditya]: Add `features/skill_schema.feature` scenarios validating each example and invalid cases. + - [ ] Tests (Robot) [Aditya]: Add `robot/skill_schema.robot` to load and validate every example. + - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/skill_schema_bench.py` for schema validation throughput. + - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Aditya]: `git commit -m "docs(skill): add skill yaml schema and examples"`. +- [ ] **COMMIT (Owner: Jeff | Group: C0.skill.domain) - Commit message: "feat(skill): add skill domain model and resolver"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Add `Skill`, `SkillItem`, `SkillToolRef`, `SkillInclude`, and `SkillInlineTool` models in `src/cleveragents/domain/models/core/skill.py` with namespaced naming rules. + - [ ] Code [Jeff]: Implement `SkillResolver` to flatten includes into ordered tool lists, de-duplicate tools, and reject cycles with path traces. + - [ ] Code [Jeff]: Add `Skill.resolve_tools()` returning resolved tool/validation names plus inline tool definitions for compiler use. + - [ ] Docs [Jeff]: Add `docs/reference/skill_model.md` and `docs/reference/skill_resolution.md` with resolution order examples. + - [ ] Tests (Behave) [Jeff]: Add `features/skill_resolution.feature` for include ordering, de-dupe rules, and cycle detection. + - [ ] Tests (Robot) [Jeff]: Add `robot/skill_resolution.robot` smoke tests for resolver output. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/skill_resolution_bench.py` for resolver performance. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(skill): add skill domain model and resolver"`. +- [ ] **COMMIT (Owner: Luis | Group: C0.skill.registry) - Commit message: "feat(skill): add skill registry persistence"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add `skills` and `skill_items` tables (namespaced name PK, description, source, yaml_text, timestamps) with indexes on namespace/name. + - [ ] Code [Luis]: Implement `SkillRepository` CRUD + list filters and `SkillRegistryService` with add/update/remove/show/list. + - [ ] Code [Luis]: Enforce referential integrity for included skills and tool references at registration time. + - [ ] Docs [Luis]: Add `docs/reference/skill_registry.md` with registration and update behavior. + - [ ] Tests (Behave) [Luis]: Add `features/skill_registry.feature` for add/update/remove and invalid include cases. + - [ ] Tests (Robot) [Luis]: Add `robot/skill_registry.robot` CLI/service smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/skill_registry_bench.py` for registry list performance. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(skill): add skill registry persistence"`. +- [ ] **COMMIT (Owner: Rui | Group: C0.skill.cli) - Commit message: "feat(cli): add skill commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Rui]: Implement `agents skill add/remove/list/show/tools` with YAML config input and `--namespace` filter. + - [ ] Code [Rui]: Ensure `skill tools` shows resolved tool list, inline tool IDs, and validation nodes. + - [ ] Docs [Rui]: Update CLI reference with skill commands, examples, and output fields. + - [ ] Tests (Behave) [Rui]: Add CLI scenarios for skill add/show/tools/list/remove. + - [ ] Tests (Robot) [Rui]: Add `robot/skill_cli.robot` for end-to-end CLI flows. + - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/skill_cli_bench.py` for config parsing overhead. + - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "feat(cli): add skill commands"`. + +**Parallel Group C0.runtime: Tool Lifecycle Runtime [Jeff]** (depends on C0.domain + C0.registry; must land before C3.context) +- [ ] **COMMIT (Owner: Jeff | Group: C0.runtime) - Commit message: "feat(tool): add tool lifecycle runtime"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Implement `ToolRuntime`/`ToolInstance` interfaces with `discover/activate/execute/deactivate` hooks and lifecycle state tracking. + - [ ] Code [Jeff]: Add `ToolExecutionContext` with resolved resource bindings, sandbox paths, plan metadata, and cancellation token. + - [ ] Code [Jeff]: Add lifecycle cache with per-plan activation reuse and guaranteed `deactivate` on plan completion/cancel. + - [ ] Code [Jeff]: Enforce tool capability flags (read-only/writes/checkpointable) and read-only plan gating at runtime. + - [ ] Docs [Jeff]: Add `docs/reference/tool_lifecycle.md` describing hook ordering and failure handling. + - [ ] Tests (Behave) [Jeff]: Add lifecycle scenarios for activate/execute/deactivate ordering and error propagation. + - [ ] Tests (Robot) [Jeff]: Add `robot/tool_lifecycle.robot` runtime smoke tests. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/tool_lifecycle_bench.py` for lifecycle overhead. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(tool): add tool lifecycle runtime"`. + **Parallel Group C1: Actor Schema & Examples [Aditya + Jeff]** (start Day 5; C2 depends on this) - [ ] **COMMIT (Owner: Aditya | Group: C1.schema) - Commit message: "feat(actor): add actor yaml schema models"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Aditya]: Add schema models (ActorType, NodeType, ContextView, ToolDefinition, RouteDefinition, ActorConfigSchema) with strict validation in `src/cleveragents/actor/schema.py`. - [ ] Code [Aditya]: Add tool-node schema fields that reference Tool Registry names, including validation nodes, and require input/output schema presence. - [ ] Code [Aditya]: Add YAML load/serialize helpers and schema version guard. - [ ] Docs [Aditya]: Add `docs/reference/actors_schema.md` with field definitions, tool node semantics, and graph constraints. - - [ ] Tests (Behave) [Rui]: Add `features/actor_schema.feature` scenarios for validation and topology errors. - - [ ] Tests (Robot) [Rui]: Add `robot/actor_schema.robot` YAML load smoke test. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_schema_bench.py` for YAML validation cost. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Aditya]: Add `features/actor_schema.feature` scenarios for validation and topology errors. + - [ ] Tests (Robot) [Aditya]: Add `robot/actor_schema.robot` YAML load smoke test. + - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/actor_schema_bench.py` for YAML validation cost. + - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Aditya]: `git commit -m "feat(actor): add actor yaml schema models"`. - [ ] **COMMIT (Owner: Aditya | Group: C1.examples) - Commit message: "docs(actor): add actor yaml examples"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Docs [Aditya]: Add `docs/reference/actors_examples.md` with strategist, executor, reviewer, tool-only, validation-node, and graph YAML examples. - [ ] Docs [Aditya]: Store example YAML files under `examples/actors/` for automated tests. - - [ ] Tests (Behave) [Rui]: Add `features/actor_examples.feature` to ensure all examples validate. - - [ ] Tests (Robot) [Rui]: Add `robot/actor_examples.robot` to load each example. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_examples_load_bench.py` for YAML load throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Aditya]: Add `features/actor_examples.feature` to ensure all examples validate. + - [ ] Tests (Robot) [Aditya]: Add `robot/actor_examples.robot` to load each example. + - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/actor_examples_load_bench.py` for YAML load throughput. + - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Aditya]: `git commit -m "docs(actor): add actor yaml examples"`. **Parallel Group C2: Actor Loading & Compilation [Aditya + Jeff]** (depends on C1) @@ -1853,42 +1883,42 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Aditya]: Implement actor loader/registry with namespaced lookup, cache invalidation, and file discovery in `actors/` and `examples/actors/`. - [ ] Code [Aditya]: Add registry integration with Tool Registry so tool nodes resolve at load time. - [ ] Docs [Aditya]: Add `docs/reference/actors_loading.md` with discovery rules and namespaces. - - [ ] Tests (Behave) [Rui]: Add `features/actor_loading.feature` for discovery, duplicates, and namespace lookup. - - [ ] Tests (Robot) [Rui]: Add `robot/actor_loading.robot` for loader smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_loading_bench.py` for registry load performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Aditya]: Add `features/actor_loading.feature` for discovery, duplicates, and namespace lookup. + - [ ] Tests (Robot) [Aditya]: Add `robot/actor_loading.robot` for loader smoke tests. + - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/actor_loading_bench.py` for registry load performance. + - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Aditya]: `git commit -m "feat(actor): add actor registry and loader"`. - [ ] **COMMIT (Owner: Jeff | Group: C2.compiler) - Commit message: "feat(actor): compile actor configs to LangGraph"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Implement ActorCompiler that builds LangGraph for LLM, TOOL, and GRAPH actors with tool node wiring. - [ ] Code [Jeff]: Resolve tool node references through Tool Registry and validate required bindings before compile. - [ ] Docs [Jeff]: Add `docs/reference/actors_compilation.md` covering compile outputs and error modes. - - [ ] Tests (Behave) [Rui]: Add `features/actor_compilation.feature` for LLM/GRAPH compilation and tool node wiring. - - [ ] Tests (Robot) [Rui]: Add `robot/actor_compilation.robot` smoke test compiling all examples. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_compilation_bench.py` for compilation speed. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/actor_compilation.feature` for LLM/GRAPH compilation and tool node wiring. + - [ ] Tests (Robot) [Jeff]: Add `robot/actor_compilation.robot` smoke test compiling all examples. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/actor_compilation_bench.py` for compilation speed. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(actor): compile actor configs to LangGraph"`. - [ ] **COMMIT (Owner: Jeff | Group: C2.refs) - Commit message: "feat(actor): resolve actor references and subgraphs"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Implement reference resolution, cycle detection, and subgraph wiring for actor refs. - [ ] Code [Jeff]: Ensure cross-namespace reference resolution follows `[server:]namespace/name` rules. - [ ] Docs [Jeff]: Update `docs/reference/actors_compilation.md` with reference semantics. - - [ ] Tests (Behave) [Rui]: Add `features/actor_reference_resolution.feature` for missing/recursive refs. - - [ ] Tests (Robot) [Rui]: Add `robot/actor_reference_resolution.robot` for subgraph wiring. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_reference_bench.py` for reference resolution performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/actor_reference_resolution.feature` for missing/recursive refs. + - [ ] Tests (Robot) [Jeff]: Add `robot/actor_reference_resolution.robot` for subgraph wiring. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/actor_reference_bench.py` for reference resolution performance. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(actor): resolve actor references and subgraphs"`. - [ ] **COMMIT (Owner: Jeff | Group: C2.legacy) - Commit message: "refactor(actor): drop v2 actor config compatibility"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Jeff]: Remove v2 JSON/YAML parsing paths in `src/cleveragents/actor/config.py` and related template engine usage. - [ ] Code [Jeff]: Ensure only v3 actor YAML schema is accepted; provide clear error message when v2 fields are present. - [ ] Docs [Jeff]: Update `docs/reference/actors_loading.md` with v3-only note and migration guidance. - - [ ] Tests (Behave) [Rui]: Add scenarios that reject v2 actor config files. - - [ ] Tests (Robot) [Rui]: Add Robot tests that attempt to load v2 configs and assert failure. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/actor_schema_reject_bench.py` for validation overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add scenarios that reject v2 actor config files. + - [ ] Tests (Robot) [Jeff]: Add Robot tests that attempt to load v2 configs and assert failure. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/actor_schema_reject_bench.py` for validation overhead. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "refactor(actor): drop v2 actor config compatibility"`. **Parallel Group C3: Skill Protocol & Context [Jeff]** (critical path; depends on C1) @@ -1897,33 +1927,33 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Jeff]: Add `SkillDefinition` model that references Tool Registry names and optional inline tool definitions. - [ ] Code [Jeff]: Add error mapping helpers to normalize tool failures into SkillError payloads. - [ ] Docs [Jeff]: Add `docs/reference/skills_protocol.md` describing metadata, tool composition, and JSON schema rules. - - [ ] Tests (Behave) [Rui]: Add `features/skill_protocol.feature` for metadata validation and error capture. - - [ ] Tests (Robot) [Rui]: Add `robot/skill_protocol.robot` smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/skill_protocol_bench.py` for validation throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/skill_protocol.feature` for metadata validation and error capture. + - [ ] Tests (Robot) [Jeff]: Add `robot/skill_protocol.robot` smoke tests. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/skill_protocol_bench.py` for validation throughput. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(skill): add skill protocol and metadata"`. - [ ] **COMMIT (Owner: Jeff | Group: C3.context) - Commit message: "feat(skill): add skill context and registry"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Implement SkillContext (plan/resource access, sandbox path, change tracker) and SkillRegistry in `src/cleveragents/skills/context.py`. - [ ] Code [Jeff]: Wire SkillRegistry to Tool Registry for tool resolution and validation node inclusion. - [ ] Code [Jeff]: Add context helpers for resolving bound resources and exposing plan metadata. - [ ] Docs [Jeff]: Add `docs/reference/skills_context.md` with context fields and helper methods. - - [ ] Tests (Behave) [Rui]: Add `features/skill_context.feature` for sandboxed access and registry resolution. - - [ ] Tests (Robot) [Rui]: Add `robot/skill_context.robot` for registry smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/skill_context_bench.py` for registry resolution overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/skill_context.feature` for sandboxed access and registry resolution. + - [ ] Tests (Robot) [Jeff]: Add `robot/skill_context.robot` for registry smoke tests. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/skill_context_bench.py` for registry resolution overhead. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(skill): add skill context and registry"`. - [ ] **COMMIT (Owner: Jeff | Group: C3.inline) - Commit message: "feat(skill): add inline tool executor"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Implement inline tool execution with timeouts and restricted environment in `src/cleveragents/skills/inline_executor.py`. - [ ] Code [Jeff]: Ensure inline tools conform to Tool Registry schema and return structured results. - [ ] Code [Jeff]: Add safeguards for file/network access inside inline tools (local-only for MVP). - [ ] Docs [Jeff]: Add `docs/reference/skills_inline.md` with safety constraints. - - [ ] Tests (Behave) [Rui]: Add `features/skill_inline.feature` for execution and timeout handling. - - [ ] Tests (Robot) [Rui]: Add `robot/skill_inline.robot` for inline tool smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/inline_tool_bench.py` for execution overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/skill_inline.feature` for execution and timeout handling. + - [ ] Tests (Robot) [Jeff]: Add `robot/skill_inline.robot` for inline tool smoke tests. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/inline_tool_bench.py` for execution overhead. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(skill): add inline tool executor"`. **Parallel Group C4: Built-in Skills [Jeff + Luis]** (depends on C3) @@ -1932,33 +1962,33 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Jeff]: Register tools in Tool Registry with resource bindings for fs/git resources and sandbox path rewrite. - [ ] Code [Jeff]: Add content size limits and encoding normalization (UTF-8) for file tools. - [ ] Docs [Jeff]: Add `docs/reference/skills_file.md` with examples and error cases. - - [ ] Tests (Behave) [Rui]: Add `features/skill_file_ops.feature` for read/write/edit/delete flows. - - [ ] Tests (Robot) [Rui]: Add `robot/skill_file_ops.robot` for file ops integration. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/file_tool_bench.py` for read/write throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/skill_file_ops.feature` for read/write/edit/delete flows. + - [ ] Tests (Robot) [Jeff]: Add `robot/skill_file_ops.robot` for file ops integration. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/file_tool_bench.py` for read/write throughput. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(skill): add file operation skills"`. - [ ] **COMMIT (Owner: Jeff | Group: C4.search) - Commit message: "feat(skill): add directory and search skills"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Implement ListDir, Glob, and Grep tools with ignore patterns and size limits. - [ ] Code [Jeff]: Register tools in Tool Registry with resource bindings and sandbox awareness. - [ ] Code [Jeff]: Enforce include/exclude glob filters from project context policies. - [ ] Docs [Jeff]: Add `docs/reference/skills_search.md` with examples. - - [ ] Tests (Behave) [Rui]: Add `features/skill_search.feature` for listing/globbing/searching. - - [ ] Tests (Robot) [Rui]: Add `robot/skill_search.robot` for search integration. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/search_tool_bench.py` for search performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/skill_search.feature` for listing/globbing/searching. + - [ ] Tests (Robot) [Jeff]: Add `robot/skill_search.robot` for search integration. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/search_tool_bench.py` for search performance. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(skill): add directory and search skills"`. - [ ] **COMMIT (Owner: Luis | Group: C4.git) - Commit message: "feat(skill): add git operation skills"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Implement read-only git tools (status, diff, log, show) for sandboxed repos. - [ ] Code [Luis]: Register git tools in Tool Registry with read-only capability metadata. - [ ] Code [Luis]: Add path guards to ensure git tools only run inside sandbox root. - [ ] Docs [Luis]: Add `docs/reference/skills_git.md` clarifying no destructive ops in MVP. - - [ ] Tests (Behave) [Rui]: Add `features/skill_git.feature` for git tool outputs. - - [ ] Tests (Robot) [Rui]: Add `robot/skill_git.robot` for git tool integration. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/git_tool_bench.py` for diff/log performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add `features/skill_git.feature` for git tool outputs. + - [ ] Tests (Robot) [Luis]: Add `robot/skill_git.robot` for git tool integration. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/git_tool_bench.py` for diff/log performance. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(skill): add git operation skills"`. **Parallel Group C5: Tool Routing & Change Tracking [Luis + Jeff]** (depends on C3/C4) @@ -1967,33 +1997,33 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Luis]: Ensure ChangeSet stores resource references, sandbox paths, tool metadata, and timestamps. - [ ] Code [Luis]: Add ChangeSet serialization helper for plan diff output (group by resource). - [ ] Docs [Luis]: Add `docs/reference/change_tracking.md` describing tool-to-change mapping. - - [ ] Tests (Behave) [Rui]: Add `features/change_tracking.feature` for ChangeSet aggregation. - - [ ] Tests (Robot) [Rui]: Add `robot/change_tracking.robot` for tracker smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/change_tracking_bench.py` for invocation tracking overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add `features/change_tracking.feature` for ChangeSet aggregation. + - [ ] Tests (Robot) [Luis]: Add `robot/change_tracking.robot` for tracker smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/change_tracking_bench.py` for invocation tracking overhead. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(change): add ChangeSet models and invocation tracker"`. - [ ] **COMMIT (Owner: Jeff | Group: C5.router) - Commit message: "feat(change): add tool router for providers"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Implement ToolCallRouter for OpenAI/Anthropic/LangChain tool schemas with deterministic IDs. - [ ] Code [Jeff]: Add mapping for tool/validation names and argument schemas based on Tool Registry metadata. - [ ] Code [Jeff]: Add tool-call result normalization to match ToolInvocation schema. - [ ] Docs [Jeff]: Add `docs/reference/tool_router.md` with provider-specific mappings. - - [ ] Tests (Behave) [Rui]: Add `features/tool_router.feature` for schema mapping. - - [ ] Tests (Robot) [Rui]: Add `robot/tool_router.robot` for routing smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/tool_router_bench.py` for routing performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/tool_router.feature` for schema mapping. + - [ ] Tests (Robot) [Jeff]: Add `robot/tool_router.robot` for routing smoke tests. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/tool_router_bench.py` for routing performance. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(change): add tool router for providers"`. - [ ] **COMMIT (Owner: Luis | Group: C5.diff) - Commit message: "feat(change): add diff review artifacts"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Implement DiffBuilder and ReviewArtifact models for CLI review. - [ ] Code [Luis]: Add support for multi-resource diffs and per-resource grouping. - [ ] Code [Luis]: Add diff output serializers for rich/plain/json formats. - [ ] Docs [Luis]: Add `docs/reference/diff_review.md` with output format. - - [ ] Tests (Behave) [Rui]: Add `features/diff_review.feature` for diff generation. - - [ ] Tests (Robot) [Rui]: Add `robot/diff_review.robot` for review artifacts. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/diff_review_bench.py` for diff building performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add `features/diff_review.feature` for diff generation. + - [ ] Tests (Robot) [Luis]: Add `robot/diff_review.robot` for review artifacts. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/diff_review_bench.py` for diff building performance. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(change): add diff review artifacts"`. **Parallel Group C6: Validation Pipeline [Luis + Jeff]** (depends on C5 and validation attachment config) @@ -2003,22 +2033,33 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Luis]: Enforce required vs informational validation modes and fix-then-revalidate loop hooks. - [ ] Code [Luis]: Persist validation summary into Plan metadata for later review. - [ ] Docs [Luis]: Add `docs/reference/validation_pipeline.md` with ordering, timeouts, and failure handling. - - [ ] Tests (Behave) [Rui]: Add `features/validation_pipeline.feature` for pass/fail paths and required/informational modes. - - [ ] Tests (Robot) [Rui]: Add `robot/validation_pipeline.robot` for pipeline smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/validation_pipeline_bench.py` for pipeline runtime. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add `features/validation_pipeline.feature` for pass/fail paths and required/informational modes. + - [ ] Tests (Robot) [Luis]: Add `robot/validation_pipeline.robot` for pipeline smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/validation_pipeline_bench.py` for pipeline runtime. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(validation): add validation pipeline and results model"`. +- [ ] **COMMIT (Owner: Jeff | Group: C6.wraps) - Commit message: "feat(validation): support wrapped tools and transforms"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) + - [ ] Code [Jeff]: Implement validation `wraps` execution path that runs the wrapped Tool and captures its output. + - [ ] Code [Jeff]: Add transform engine that maps wrapped tool output into ValidationResult schema (must output `passed` boolean). + - [ ] Code [Jeff]: Enforce read-only constraints for validations even when wrapping write-capable tools; block if violation. + - [ ] Docs [Jeff]: Update `docs/reference/validation_model.md` with `wraps` + `transform` examples and safety rules. + - [ ] Tests (Behave) [Jeff]: Add wrapped-validation scenarios with transform success/fail paths. + - [ ] Tests (Robot) [Jeff]: Add `robot/validation_wraps.robot` for wrapped validation end-to-end. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/validation_wraps_bench.py` for transform overhead. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(validation): support wrapped tools and transforms"`. - [ ] **COMMIT (Owner: Jeff | Group: C6.gating) - Commit message: "feat(validation): integrate validation with apply gating"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Block apply on required validation failure; surface validation artifacts for review. - [ ] Code [Jeff]: Ensure informational validation failures do not block apply but are logged in plan status. - [ ] Code [Jeff]: Add CLI status output for validation summary (required vs informational counts). - [ ] Docs [Jeff]: Update `docs/reference/plan_actor_integration.md` with validation gating behavior. - - [ ] Tests (Behave) [Rui]: Add `features/validation_gating.feature` for apply blocking. - - [ ] Tests (Robot) [Rui]: Add `robot/validation_gating.robot` for end-to-end gating. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/validation_gating_bench.py` for gating overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/validation_gating.feature` for apply blocking. + - [ ] Tests (Robot) [Jeff]: Add `robot/validation_gating.robot` for end-to-end gating. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/validation_gating_bench.py` for gating overhead. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(validation): integrate validation with apply gating"`. **Parallel Group C7: MCP Adapter [Aditya]** (depends on C3) @@ -2027,11 +2068,11 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Aditya]: Register MCP tools in Tool Registry with dynamic discovery from MCP server. - [ ] Code [Aditya]: Add timeout and retry defaults for MCP calls (local-only for MVP). - [ ] Docs [Aditya]: Add `docs/reference/skills_mcp.md` with server connection examples. - - [ ] Tests (Behave) [Rui]: Add `features/skill_mcp.feature` for MCP tool calls. - - [ ] Tests (Robot) [Rui]: Add `robot/skill_mcp.robot` for MCP adapter smoke test. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/mcp_adapter_bench.py` for tool invocation latency. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Aditya]: Add `features/skill_mcp.feature` for MCP tool calls. + - [ ] Tests (Robot) [Aditya]: Add `robot/skill_mcp.robot` for MCP adapter smoke test. + - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/mcp_adapter_bench.py` for tool invocation latency. + - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Aditya]: `git commit -m "feat(skill): add MCP adapter for external tools"`. **Parallel Group C8: Built-in Provider Actors [Aditya]** (depends on C1/C2) @@ -2039,11 +2080,11 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Aditya]: Add built-in actor configs for `openai/`, `anthropic/`, and `openrouter/` (plus `google/` if configured). - [ ] Code [Aditya]: Add built-in actors for invariant reconciliation and estimation roles (using provider defaults). - [ ] Docs [Aditya]: Add `docs/reference/provider_actors.md` with provider defaults. - - [ ] Tests (Behave) [Rui]: Add `features/provider_actors.feature` for built-in actor loading. - - [ ] Tests (Robot) [Rui]: Add `robot/provider_actors.robot` for registry visibility. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/provider_actor_load_bench.py` for registry load cost. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Aditya]: Add `features/provider_actors.feature` for built-in actor loading. + - [ ] Tests (Robot) [Aditya]: Add `robot/provider_actors.robot` for registry visibility. + - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/provider_actor_load_bench.py` for registry load cost. + - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Aditya]: `git commit -m "feat(actor): add built-in provider actors"`. **Parallel Group C9: Plan-Actor Integration [Jeff + Luis]** (depends on C2/C5/C6) @@ -2053,22 +2094,22 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Jeff]: Ensure Execute uses sandbox resources and tool calls routed through Tool Router + ChangeSet. - [ ] Code [Jeff]: Add plan status updates for phase start/complete/fail during actor execution. - [ ] Docs [Jeff]: Add `docs/reference/plan_actor_integration.md` with phase flow. - - [ ] Tests (Behave) [Rui]: Add `features/plan_actor_integration.feature` for strategy/execute flows. - - [ ] Tests (Robot) [Rui]: Add `robot/plan_actor_integration.robot` for end-to-end actor execution. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_actor_integration_bench.py` for execution overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/plan_actor_integration.feature` for strategy/execute flows. + - [ ] Tests (Robot) [Jeff]: Add `robot/plan_actor_integration.robot` for end-to-end actor execution. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/plan_actor_integration_bench.py` for execution overhead. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(plan): execute strategize and execute phases via actors"`. - [ ] **COMMIT (Owner: Jeff | Group: C9.apply) - Commit message: "feat(plan): integrate change review and apply flow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Wire ChangeSet review artifacts into `plan diff` and review-before-apply flow. - [ ] Code [Jeff]: Ensure Apply merges sandbox into real resources only after required validations pass. - [ ] Code [Jeff]: Persist apply summary (files changed, validations) back into Plan metadata for `plan status`. - [ ] Docs [Jeff]: Update CLI docs for `plan diff` and `plan apply` review output. - - [ ] Tests (Behave) [Rui]: Add `features/plan_review_apply.feature` for review gate behavior. - - [ ] Tests (Robot) [Rui]: Add `robot/plan_review_apply.robot` for review-before-apply path. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_apply_bench.py` for apply throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add `features/plan_review_apply.feature` for review gate behavior. + - [ ] Tests (Robot) [Jeff]: Add `robot/plan_review_apply.robot` for review-before-apply path. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/plan_apply_bench.py` for apply throughput. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(plan): integrate change review and apply flow"`. **M3 SUCCESS CRITERIA**: @@ -2088,30 +2129,31 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target ### Section 6: Execution Pipeline, Decisions & Invariants [M3-M4] **Target: Milestone M4 (+21 days)** +**Week 3 focus**: decision capture, correction, invariants, and DoD gating. **Parallel Group D1: Decision Domain [Hamza + Rui]** (foundation for D2-D5) - [ ] **COMMIT (Owner: Hamza | Group: D1.domain) - Commit message: "feat(domain): add decision model and context snapshots"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Add `DecisionType`, `ContextSnapshot`, and `Decision` models with correction fields and helpers. - [ ] Code [Hamza]: Include required fields: question, chosen option, alternatives, confidence score, rationale, dependencies, and context hash. - [ ] Docs [Hamza]: Add `docs/reference/decision_model.md` with examples and schema notes. - - [ ] Tests (Behave) [Rui]: Add `features/decision_model.feature` for validation and helpers. - - [ ] Tests (Robot) [Rui]: Add `robot/decision_model.robot` smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_model_bench.py` for decision validation throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add `features/decision_model.feature` for validation and helpers. + - [ ] Tests (Robot) [Hamza]: Add `robot/decision_model.robot` smoke tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/decision_model_bench.py` for decision validation throughput. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(domain): add decision model and context snapshots"`. -**Parallel Group D2: Decision Recording Service [Hamza + Luis]** (depends on D1) +**Parallel Group D2: Decision Recording Service [Hamza]** (depends on D1) - [ ] **COMMIT (Owner: Hamza | Group: D2.service) - Commit message: "feat(service): add decision recording and snapshot store"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Implement `DecisionService` with `record_decision`, sequence numbers, tree queries, and downstream linking. - [ ] Code [Hamza]: Add `ContextSnapshotStore` interface with a file-backed MVP implementation and hash dedupe. - - [ ] Code [Luis]: Integrate decision recording into strategize/execute phases (prompt/strategy/subplan/tool decisions). + - [ ] Code [Hamza]: Integrate decision recording into strategize/execute phases (prompt/strategy/subplan/tool decisions). - [ ] Docs [Hamza]: Add `docs/reference/decision_service.md` covering recording and snapshots. - - [ ] Tests (Behave) [Rui]: Add `features/decision_recording.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add `robot/decision_recording.robot` integration smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_recording_bench.py` for record throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add `features/decision_recording.feature` scenarios. + - [ ] Tests (Robot) [Hamza]: Add `robot/decision_recording.robot` integration smoke tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/decision_recording_bench.py` for record throughput. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(service): add decision recording and snapshot store"`. **Parallel Group D3: Decision CLI & Viewing [Hamza + Rui]** (depends on D1/D2) @@ -2119,11 +2161,11 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Hamza]: Implement `plan tree` and `plan explain` with rich/json/flat formats and `--show-superseded`/`--show-context`. - [ ] Code [Hamza]: Add `--show-reasoning` to include confidence and alternatives in explain output per spec. - [ ] Docs [Hamza]: Update CLI reference for decision viewing commands. - - [ ] Tests (Behave) [Rui]: Add tree/explain scenarios including superseded handling. - - [ ] Tests (Robot) [Rui]: Add `robot/decision_cli.robot` smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_cli_bench.py` for tree rendering overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add tree/explain scenarios including superseded handling. + - [ ] Tests (Robot) [Hamza]: Add `robot/decision_cli.robot` smoke tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/decision_cli_bench.py` for tree rendering overhead. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(cli): add plan tree and explain commands"`. **Parallel Group D4: Decision Correction [Jeff + Luis]** (depends on D2/D3) @@ -2132,21 +2174,21 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Jeff]: Revert flow with checkpoint rollback, supersede downstream decisions, and subtree re-exec. - [ ] Code [Jeff]: Persist correction attempt IDs and link them to superseded decisions. - [ ] Docs [Jeff]: Add `docs/reference/decision_correction.md` for revert behavior. - - [ ] Tests (Behave) [Rui]: Add revert + dry-run scenarios. - - [ ] Tests (Robot) [Rui]: Add revert integration tests with checkpoint rollback. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_correction_revert_bench.py` for correction overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add revert + dry-run scenarios. + - [ ] Tests (Robot) [Jeff]: Add revert integration tests with checkpoint rollback. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/decision_correction_revert_bench.py` for correction overhead. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(service): add decision correction revert flow"`. - [ ] **COMMIT (Owner: Jeff | Group: D4.append) - Commit message: "feat(service): add decision correction append flow"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Append flow creating fix subplan without rewriting history; link correction attempt + decision tree updates. - [ ] Code [Jeff]: Record append corrections as separate subtree with explicit lineage. - [ ] Docs [Jeff]: Extend correction docs for append mode and guidance-file usage. - - [ ] Tests (Behave) [Rui]: Add append correction scenarios. - - [ ] Tests (Robot) [Rui]: Add append correction smoke test. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_correction_append_bench.py` for append overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add append correction scenarios. + - [ ] Tests (Robot) [Jeff]: Add append correction smoke test. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/decision_correction_append_bench.py` for append overhead. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(service): add decision correction append flow"`. **Parallel Group D5: Decision Persistence [Hamza + Luis]** (depends on D1) @@ -2154,38 +2196,38 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Hamza]: Add Alembic migrations for `decisions` and `context_snapshots` with indexes. - [ ] Code [Hamza]: Add indexes for plan_id, decision_type, and superseded flags for fast tree queries. - [ ] Docs [Hamza]: Update `docs/reference/database_schema.md` with decision tables. - - [ ] Tests (Behave) [Rui]: Add migration verification scenarios. - - [ ] Tests (Robot) [Rui]: Add DB migration smoke test. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_migration_bench.py` for migration baseline. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add migration verification scenarios. + - [ ] Tests (Robot) [Hamza]: Add DB migration smoke test. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/decision_migration_bench.py` for migration baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(db): add decision tables"`. - [ ] **COMMIT (Owner: Hamza | Group: D5.repo) - Commit message: "feat(repo): add decision repositories"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Implement DecisionRepository + ContextSnapshotRepository with tree queries and max-sequence helpers. - [ ] Code [Hamza]: Add repository methods for superseded decision lookup and subtree retrieval. - [ ] Docs [Hamza]: Document repository interfaces in `docs/reference/repositories.md`. - - [ ] Tests (Behave) [Rui]: Add decision persistence scenarios (create/query/superseded). - - [ ] Tests (Robot) [Rui]: Add repository integration smoke test. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_repository_bench.py` for tree query performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add decision persistence scenarios (create/query/superseded). + - [ ] Tests (Robot) [Hamza]: Add repository integration smoke test. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/decision_repository_bench.py` for tree query performance. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(repo): add decision repositories"`. - [ ] **COMMIT (Owner: Luis | Group: D5.di) - Commit message: "feat(di): wire decision services"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Wire decision repositories + services into DI and CLI. - [ ] Docs [Luis]: Update DI docs for decision wiring. - - [ ] Tests (Behave) [Rui]: Add DI wiring scenarios for decision commands. - - [ ] Tests (Robot) [Rui]: Add CLI smoke test using persisted decisions. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_di_bench.py` for DI resolution overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add DI wiring scenarios for decision commands. + - [ ] Tests (Robot) [Luis]: Add CLI smoke test using persisted decisions. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/decision_di_bench.py` for DI resolution overhead. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(di): wire decision services"`. - [ ] **COMMIT (Owner: Rui | Group: D5.tests) - Commit message: "test(persistence): add decision persistence suites"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Tests (Behave) [Rui]: Add `features/decision_persistence.feature` scenarios. - [ ] Tests (Robot) [Rui]: Add `robot/decision_persistence.robot` E2E coverage. - [ ] Docs [Rui]: Update `docs/development/testing.md` with decision suites. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/decision_persistence_bench.py` for DB persistence throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Rui]: `git commit -m "test(persistence): add decision persistence suites"`. **Parallel Group DOD: Definition of Done + Invariants [Luis + Jeff]** (depends on D2/D4) @@ -2193,29 +2235,29 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Luis]: Evaluate `definition_of_done` before apply; block apply with clear error if unmet. - [ ] Code [Luis]: Ensure DoD templating uses plan arguments and preserves template in plan metadata. - [ ] Docs [Luis]: Add `docs/reference/definition_of_done.md` with examples. - - [ ] Tests (Behave) [Rui]: Add DoD pass/fail scenarios. - - [ ] Tests (Robot) [Rui]: Add DoD integration smoke test. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/dod_evaluation_bench.py` for evaluation overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add DoD pass/fail scenarios. + - [ ] Tests (Robot) [Luis]: Add DoD integration smoke test. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/dod_evaluation_bench.py` for evaluation overhead. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(dod): enforce definition-of-done gating"`. - [ ] **COMMIT (Owner: Jeff | Group: DOD.invariants) - Commit message: "feat(invariant): add invariant models and enforcement"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Add invariant models, merge order (plan > project > action > global), and enforcement before strategize. - [ ] Code [Jeff]: Add Invariant Reconciliation Actor role and record `invariant_enforced` decisions. - [ ] Code [Jeff]: Add `agents invariant add/list/remove` CLI with scope flags. - [ ] Docs [Jeff]: Add `docs/reference/invariants.md` and update CLI reference. - - [ ] Tests (Behave) [Rui]: Add invariant merge + violation scenarios. - - [ ] Tests (Robot) [Rui]: Add invariant CLI integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/invariant_merge_bench.py` for merge overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add invariant merge + violation scenarios. + - [ ] Tests (Robot) [Jeff]: Add invariant CLI integration tests. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/invariant_merge_bench.py` for merge overhead. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(invariant): add invariant models and enforcement"`. ---- ### Section 7: Subplans & Parallelism [M5] **Target: Milestone M5 (+25 days)** +**Week 3-4 focus**: subplan spawning, parallel execution, and result merging. **Parallel Group E1: Subplan Domain [Luis + Rui]** - [ ] **COMMIT (Owner: Luis | Group: E1.domain) - Commit message: "feat(domain): add subplan config and status models"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) @@ -2227,11 +2269,11 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Luis]: Extend `Plan` with `subplan_config`, `subplan_statuses`, `spawn_decision_id`, and helpers (`is_subplan`, `has_subplans`, `child_count`). - [ ] Code [Luis]: Add DecisionType constants for `subplan_spawn` and `subplan_parallel_spawn` and ensure models reference them. - [ ] Docs [Luis]: Add `docs/reference/subplan_model.md`. - - [ ] Tests (Behave) [Rui]: Add `features/subplan_model.feature` scenarios for config validation, dependency cycles, and parent/root helpers. - - [ ] Tests (Robot) [Rui]: Add `robot/subplan_model.robot` smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_model_bench.py` for model validation. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add `features/subplan_model.feature` scenarios for config validation, dependency cycles, and parent/root helpers. + - [ ] Tests (Robot) [Luis]: Add `robot/subplan_model.robot` smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/subplan_model_bench.py` for model validation. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(domain): add subplan config and status models"`. **Parallel Group E2: Subplan Spawning [Jeff + Aditya]** (depends on D2 + E1) @@ -2242,22 +2284,22 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Jeff]: Persist subplan config into child Plan metadata (`subplan_config`) and link `spawn_decision_id`. - [ ] Code [Jeff]: Link SUBPLAN_SPAWN decisions to created subplans and status tracking. - [ ] Docs [Jeff]: Add `docs/reference/subplan_service.md`. - - [ ] Tests (Behave) [Rui]: Add subplan spawn scenarios (inheritance, overrides, dependency ordering). - - [ ] Tests (Robot) [Rui]: Add subplan spawn integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_spawn_bench.py` for spawn throughput. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add subplan spawn scenarios (inheritance, overrides, dependency ordering). + - [ ] Tests (Robot) [Jeff]: Add subplan spawn integration tests. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/subplan_spawn_bench.py` for spawn throughput. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(service): add subplan service and spawn workflow"`. - [ ] **COMMIT (Owner: Aditya | Group: E2.actor) - Commit message: "feat(actor): add plan_subplan tool and decision emission"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Aditya]: Add `plan_subplan` tool to strategy actors and emit SUBPLAN_SPAWN decisions. - [ ] Code [Aditya]: Support `parallel=true` to emit SUBPLAN_PARALLEL_SPAWN and include dependency list. - [ ] Code [Aditya]: Include merge strategy, resource scope, and context view overrides in decision payload. - [ ] Docs [Aditya]: Update actor YAML examples for subplan emission. - - [ ] Tests (Behave) [Rui]: Add scenarios for subplan decision emission (parallel + dependencies). - - [ ] Tests (Robot) [Rui]: Add actor tool integration smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_actor_tool_bench.py` for tool invocation overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Aditya]: Add scenarios for subplan decision emission (parallel + dependencies). + - [ ] Tests (Robot) [Aditya]: Add actor tool integration smoke tests. + - [ ] Tests (ASV) [Aditya]: Add `asv/benchmarks/subplan_actor_tool_bench.py` for tool invocation overhead. + - [ ] Quality [Aditya]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Aditya]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Aditya]: `git commit -m "feat(actor): add plan_subplan tool and decision emission"`. **Parallel Group E3: Parallel Execution [Luis + Jeff]** (depends on E1/E2) @@ -2267,244 +2309,59 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Luis]: Track status updates for subplans and propagate to parent plan (processing/complete/errored). - [ ] Code [Luis]: Add cancellation propagation from parent to child subplans. - [ ] Docs [Luis]: Add `docs/reference/subplan_execution.md`. - - [ ] Tests (Behave) [Rui]: Add parallel + dependency execution scenarios. - - [ ] Tests (Robot) [Rui]: Add parallel execution integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_scheduler_bench.py` for scheduler overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add parallel + dependency execution scenarios. + - [ ] Tests (Robot) [Luis]: Add parallel execution integration tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/subplan_scheduler_bench.py` for scheduler overhead. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(service): add subplan scheduler and execution"`. -**Parallel Group E4: Result Merging [Jeff + Luis]** (depends on E3) +**Parallel Group E4: Result Merging [Jeff]** (depends on E3) - [ ] **COMMIT (Owner: Jeff | Group: E4.merge) - Commit message: "feat(merge): add subplan merge strategies"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Add three-way merge strategy for file changes and conflict markers. - - [ ] Code [Luis]: Add sequential merge and JSON merge strategies; expose merge result artifacts. + - [ ] Code [Jeff]: Add sequential merge and JSON merge strategies; expose merge result artifacts. - [ ] Code [Jeff]: Add conflict artifact model (file_path, conflict_type, base/left/right snippets). - - [ ] Code [Luis]: Store merge output as ChangeSet and attach to parent plan for review. + - [ ] Code [Jeff]: Store merge output as ChangeSet and attach to parent plan for review. - [ ] Docs [Jeff]: Add `docs/reference/subplan_merge.md`. - - [ ] Tests (Behave) [Rui]: Add merge + conflict scenarios. - - [ ] Tests (Robot) [Rui]: Add merge integration tests for multi-subplan plans. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/subplan_merge_bench.py` for merge performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add merge + conflict scenarios. + - [ ] Tests (Robot) [Jeff]: Add merge integration tests for multi-subplan plans. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/subplan_merge_bench.py` for merge performance. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(merge): add subplan merge strategies"`. -**Parallel Group E5: Multi-Project Plans [Hamza + Luis]** (depends on E2/E4) +**Parallel Group E5: Multi-Project Plans [Hamza]** (depends on E2/E4) - [ ] **COMMIT (Owner: Hamza | Group: E5.multi) - Commit message: "feat(plan): add multi-project subplan support"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Allow plans to target multiple projects with separate resource link contexts. - - [ ] Code [Luis]: Ensure sandbox isolation and cross-project dependency resolution. + - [ ] Code [Hamza]: Ensure sandbox isolation and cross-project dependency resolution. - [ ] Code [Hamza]: Add plan metadata to track project-specific ChangeSets and validation summaries. - [ ] Docs [Hamza]: Add `docs/reference/multi_project_plans.md`. - - [ ] Tests (Behave) [Rui]: Add multi-project subplan scenarios. - - [ ] Tests (Robot) [Rui]: Add multi-project integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/multi_project_bench.py` for multi-project overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add multi-project subplan scenarios. + - [ ] Tests (Robot) [Hamza]: Add multi-project integration tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/multi_project_bench.py` for multi-project overhead. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(plan): add multi-project subplan support"`. -- [ ] **Stage E5: Multi-Project Plans** (Day 25) **[Hamza]** - - **SEQUENTIAL ORDER**: E5.1 (Model extension) → E5.2 (Strategy changes) → E5.3 (Apply per project) → E5.4 (Cross-project deps) → E5.5 (Tests) - - - [ ] **E5.1** [Hamza] Extend Plan model for multiple projects: - - [ ] **E5.1a** [Hamza] Add projects field to Plan: - ```python - # In Plan model - project_ids: list[str] = Field( - default_factory=list, - description="Project IDs this plan targets" - ) - - @property - def is_multi_project(self) -> bool: - """Check if plan targets multiple projects.""" - return len(self.project_ids) > 1 - ``` - - [ ] Commit: "feat(domain): add multi-project support to Plan" - - [ ] **E5.1b** [Hamza] Add per-project sandbox tracking: - ```python - project_sandboxes: dict[str, str] = Field( - default_factory=dict, - description="Map of project_id to sandbox_id" - ) - - project_apply_status: dict[str, ApplyStatus] = Field( - default_factory=dict, - description="Apply status per project" - ) - ``` - - [ ] Commit: "feat(domain): add per-project sandbox tracking" - - [ ] **E5.2** [Hamza] Update strategy to include project assignments: - - [ ] **E5.2a** [Hamza] Add project field to subplan decisions: - ```python - # Strategy actor can specify project for subplan - context.create_subplan_decision( - action_name="local/code-fix", - description="Fix auth in backend", - target_files=["src/auth/*.py"], - target_project="backend" # New field - ) - ``` - - [ ] Commit: "feat(actor): add project targeting to subplan decisions" - - [ ] **E5.2b** [Hamza] Build cross-project dependency graph: - ```python - def build_cross_project_dag( - self, - decisions: list[Decision] - ) -> CrossProjectDAG: - """Build dependency graph that spans projects.""" - dag = CrossProjectDAG() - - for decision in decisions: - project = self._get_target_project(decision) - dag.add_node(decision.decision_id, project=project) - - for dep_id in self._get_depends_on(decision): - dep_project = self._get_project_for_decision(dep_id) - dag.add_edge(dep_id, decision.decision_id) - - # Track cross-project edges - if dep_project != project: - dag.mark_cross_project_edge(dep_id, decision.decision_id) - - return dag - ``` - - [ ] Commit: "feat(service): implement cross-project dependency tracking" - - [ ] **E5.3** [Hamza] Apply commits per project: - - [ ] **E5.3a** [Hamza] Implement per-project apply: - ```python - async def apply_multi_project( - self, - plan: Plan - ) -> MultiProjectApplyResult: - """Apply changes to each project separately.""" - results = {} - - for project_id in plan.project_ids: - try: - result = await self._apply_single_project(plan, project_id) - results[project_id] = ApplyStatus.SUCCESS - except Exception as e: - results[project_id] = ApplyStatus.FAILED - logger.error(f"Apply failed for project {project_id}: {e}") - - # Don't fail others unless they depend on this one - if self._has_dependents(project_id, plan): - logger.warning(f"Dependents of {project_id} will also fail") - - return MultiProjectApplyResult( - project_statuses=results, - fully_applied=all(s == ApplyStatus.SUCCESS for s in results.values()) - ) - ``` - - [ ] Commit: "feat(service): implement per-project apply" - - [ ] **E5.3b** [Hamza] Support partial apply: - ```python - async def apply_partial( - self, - plan: Plan, - project_ids: list[str] - ) -> MultiProjectApplyResult: - """Apply only to specified projects.""" - # Validate selected projects are valid - invalid = set(project_ids) - set(plan.project_ids) - if invalid: - raise InvalidProjectError(f"Projects not in plan: {invalid}") - - # Check dependency constraints - for pid in project_ids: - deps = self._get_project_dependencies(plan, pid) - missing_deps = deps - set(project_ids) - if missing_deps: - raise DependencyNotAppliedError( - f"Project {pid} depends on unapplied: {missing_deps}" - ) - - return await self._apply_projects(plan, project_ids) - ``` - - [ ] Commit: "feat(service): implement partial project apply" - - [ ] **E5.4** [Hamza] Handle cross-project dependencies: - - [ ] **E5.4a** [Hamza] Enforce dependency order in apply: - ```python - def get_project_apply_order( - self, - plan: Plan - ) -> list[str]: - """Get order to apply projects respecting dependencies.""" - dag = self._build_project_dependency_dag(plan) - return dag.topological_sort() - - async def apply_in_dependency_order( - self, - plan: Plan - ) -> MultiProjectApplyResult: - """Apply projects in correct dependency order.""" - order = self.get_project_apply_order(plan) - results = {} - - for project_id in order: - # Check all dependencies succeeded - deps = self._get_project_dependencies(plan, project_id) - if not all(results.get(d) == ApplyStatus.SUCCESS for d in deps): - results[project_id] = ApplyStatus.SKIPPED_DEPENDENCY_FAILED - continue - - # Apply this project - result = await self._apply_single_project(plan, project_id) - results[project_id] = result - - return MultiProjectApplyResult(project_statuses=results) - ``` - - [ ] Commit: "feat(service): implement dependency-ordered project apply" - - [ ] **E5.5** [Rui] Write end-to-end tests for multi-project: - - [ ] **E5.5a** [Rui] Multi-project scenarios: - - [ ] Scenario: Plan targets two projects with separate sandboxes - - [ ] Given plan with project_ids=[proj1, proj2] - - [ ] When plan executes - - [ ] Then each project has separate sandbox - - [ ] Scenario: Changes applied to each project separately - - [ ] Given completed multi-project plan - - [ ] When apply is called - - [ ] Then proj1 gets its changes - - [ ] And proj2 gets its changes - - [ ] Commit: "test(behave): add multi-project scenarios" - - [ ] **E5.5b** [Rui] Cross-project dependency scenarios: - - [ ] Scenario: Dependent project waits for dependency - - [ ] Given proj2 depends on proj1 changes - - [ ] When apply runs - - [ ] Then proj1 is applied first - - [ ] And proj2 is applied after proj1 succeeds - - [ ] Scenario: Dependent project skipped if dependency fails - - [ ] Given proj2 depends on proj1 - - [ ] And proj1 apply fails - - [ ] Then proj2 is marked SKIPPED_DEPENDENCY_FAILED - - [ ] Commit: "test(behave): add cross-project dependency scenarios" -**M5 SUCCESS CRITERIA** (Day 25): -- [ ] Plans can spawn subplans from SUBPLAN_SPAWN decisions -- [ ] Sequential subplan execution works (execute in order) -- [ ] Parallel subplan execution works (concurrent with max_parallel limit) -- [ ] Dependency-ordered execution respects DAG -- [ ] Results from multiple subplans merge correctly using three-way merge -- [ ] Merge conflicts are marked and can be resolved -- [ ] Plans can target multiple projects with separate sandboxes -- [ ] Cross-project dependencies handled correctly -- [ ] Large task (10+ subplans) completes successfully +### Section 8: Large Project Autonomy & Context [M6] ---- +**Target: Milestone M6 (+30 days)** +**Local-mode only**: large-project autonomy is required; server connectivity remains stubbed. -**Parallel Group G1: Large-Project Decomposition [Jeff + Luis]** +**Parallel Group G1: Large-Project Decomposition [Jeff]** - [ ] **COMMIT (Owner: Jeff | Group: G1.decompose) - Commit message: "feat(plan): add large-project decomposition and dependency closure"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Jeff]: Add hierarchical decomposition with 4+ levels and bounded context per subplan. - [ ] Code [Jeff]: Implement decomposition heuristics (max_files_per_subplan, max_tokens_per_subplan, language/dir clustering). - - [ ] Code [Luis]: Add dependency closure computation for large graphs and DAG execution ordering. - - [ ] Code [Luis]: Add bounded dependency closure with cutoff thresholds and memoization for 10K+ files. + - [ ] Code [Jeff]: Add dependency closure computation for large graphs and DAG execution ordering. + - [ ] Code [Jeff]: Add bounded dependency closure with cutoff thresholds and memoization for 10K+ files. - [ ] Code [Jeff]: Record decomposition decisions in DecisionService (strategy_choice + subplan_spawn entries). - [ ] Docs [Jeff]: Add `docs/reference/large_project_decomposition.md`. - - [ ] Tests (Behave) [Rui]: Add deep hierarchy + dependency closure scenarios. - - [ ] Tests (Robot) [Rui]: Add large-project decomposition integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/large_project_decompose_bench.py` for decomposition runtime. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Jeff]: Add deep hierarchy + dependency closure scenarios. + - [ ] Tests (Robot) [Jeff]: Add large-project decomposition integration tests. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/large_project_decompose_bench.py` for decomposition runtime. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "feat(plan): add large-project decomposition and dependency closure"`. **Parallel Group G2: Checkpointing & Rollback [Luis]** @@ -2514,11 +2371,11 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Luis]: Implement `plan rollback ` command. - [ ] Code [Luis]: Implement git-worktree checkpoint snapshots (commit hash or patch) and rollback restore. - [ ] Docs [Luis]: Add `docs/reference/checkpointing.md`. - - [ ] Tests (Behave) [Rui]: Add checkpoint/rollback scenarios. - - [ ] Tests (Robot) [Rui]: Add rollback integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/checkpoint_rollback_bench.py` for rollback latency. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add checkpoint/rollback scenarios. + - [ ] Tests (Robot) [Luis]: Add rollback integration tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/checkpoint_rollback_bench.py` for rollback latency. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(checkpoint): add checkpointing and rollback"`. **Parallel Group G3: Semantic Validation [Luis]** @@ -2527,11 +2384,11 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Luis]: Add rule registry for semantic validators (dependency cycles, API misuse, missing symbols). - [ ] Code [Luis]: Integrate semantic validation results into ValidationPipeline as informational by default. - [ ] Docs [Luis]: Add `docs/reference/semantic_validation.md`. - - [ ] Tests (Behave) [Rui]: Add semantic validation scenarios. - - [ ] Tests (Robot) [Rui]: Add semantic validation integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/semantic_validation_bench.py` for validation cost. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add semantic validation scenarios. + - [ ] Tests (Robot) [Luis]: Add semantic validation integration tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/semantic_validation_bench.py` for validation cost. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(validation): add semantic validation service"`. **Parallel Group G4: Context Tiers & Views [Hamza + Rui]** @@ -2541,11 +2398,11 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Hamza]: Add per-actor context views (strategist/executor/reviewer) and filtered presentation. - [ ] Code [Hamza]: Add summarization hook when demoting to cold tier. - [ ] Docs [Hamza]: Add `docs/reference/context_tiers.md`. - - [ ] Tests (Behave) [Rui]: Add context tier scenarios. - - [ ] Tests (Robot) [Rui]: Add context tier integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/context_tiers_bench.py` for tier lookup performance. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add context tier scenarios. + - [ ] Tests (Robot) [Hamza]: Add context tier integration tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/context_tiers_bench.py` for tier lookup performance. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(context): add hot/warm/cold tiers and actor views"`. **Parallel Group G5: Cost & Risk Estimation [Hamza]** @@ -2553,38 +2410,35 @@ MERGE POINT: Day 30 - M6 Large Project Autonomy Target - [ ] Code [Hamza]: Add optional `estimation_actor` role and cost/risk estimation outputs. - [ ] Code [Hamza]: Persist estimation output to plan metadata (cost_estimate, risk_score, duration_estimate). - [ ] Docs [Hamza]: Add `docs/reference/estimation.md` with output format. - - [ ] Tests (Behave) [Rui]: Add estimation scenarios. - - [ ] Tests (Robot) [Rui]: Add estimation integration smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/estimation_actor_bench.py` for estimation runtime. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Hamza]: Add estimation scenarios. + - [ ] Tests (Robot) [Hamza]: Add estimation integration smoke tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/estimation_actor_bench.py` for estimation runtime. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(estimation): add cost and risk estimation actor"`. -**Parallel Group G6: CLI Polish [All]** +**Parallel Group G6: CLI Polish [Jeff]** - [ ] **COMMIT (Owner: Jeff | Group: G6.cli) - Commit message: "chore(cli): polish help and output"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [All]: Standardize help text, progress indicators, and error messages with recovery hints. - - [ ] Code [All]: Ensure `--format` outputs are consistent (rich/color/table/plain/json/yaml) across core commands. - - [ ] Docs [All]: Update CLI output examples where needed. - - [ ] Tests (Robot) [Rui]: Add CLI UX smoke tests for critical commands. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/cli_render_bench.py` for output rendering overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Code [Jeff]: Standardize help text, progress indicators, and error messages with recovery hints. + - [ ] Code [Jeff]: Ensure `--format` outputs are consistent (rich/color/table/plain/json/yaml) across core commands. + - [ ] Docs [Jeff]: Update CLI output examples where needed. + - [ ] Tests (Robot) [Jeff]: Add CLI UX smoke tests for critical commands. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/cli_render_bench.py` for output rendering overhead. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Jeff]: `git commit -m "chore(cli): polish help and output"`. **--- MERGE POINT 2: Day 30 - Large Project Autonomy Target (LOCAL MODE ONLY) ---** By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is deferred): -- [ ] Handle projects with 10,000+ files using hierarchical decomposition -- [ ] Port source code from one language to another autonomously -- [ ] Use hierarchical subplans (5+ levels deep) for large tasks -- [ ] Correct decisions at any point without full re-execution -- [ ] Operate entirely in local mode (server client stubs in place but not implemented) +- Handle projects with 10,000+ files using hierarchical decomposition. +- Port source code from one language to another autonomously. +- Use hierarchical subplans (5+ levels deep) for large tasks. +- Correct decisions at any point without full re-execution. +- Operate entirely in local mode (server client stubs in place but not implemented). -**Note**: Server connectivity (M7 as redefined) is deferred beyond Day 30. The server is a separate project. The Day 30 goal focuses on autonomous large project handling in local mode. ---- - -### Section 9: Full Feature Set [Days 31-35] +### Section 9: Server Connectivity (Stubs Only) [Beyond Day 30] **Target: Milestone M7 (+35 days)** @@ -2593,11 +2447,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Luis]: Add protocol stubs for `ServerClient`, `RemoteExecutionClient`, and `AuthClient` with NotImplementedError. - [ ] Code [Luis]: Add `agents connect ` CLI stub in `cli/commands/server_client.py`. - [ ] Docs [Luis]: Add `docs/reference/server_client_stubs.md` noting client-only behavior. - - [ ] Tests (Behave) [Rui]: Add stub behavior scenarios. - - [ ] Tests (Robot) [Rui]: Add CLI stub smoke test. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/server_stub_bench.py` (baseline no-op). - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add stub behavior scenarios. + - [ ] Tests (Robot) [Luis]: Add CLI stub smoke test. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/server_stub_bench.py` (baseline no-op). + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(interfaces): add server client stubs"`. **M7 SUCCESS CRITERIA** (Post-Day 30): @@ -2606,83 +2460,6 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - Real-time updates received via WebSocket from server. - Remote projects can be specified and executed on server. -- [ ] **Stage G4: Context Tiers** (Day 34-35) **[Hamza]** - - [ ] **G4.1** [Hamza] Hot context management (10-20 files): - - [ ] Track files currently in LLM context window - - [ ] Implement LRU eviction when context limit reached - - [ ] Prioritize files based on current task relevance - - [ ] Support explicit pinning of critical files - - [ ] **G4.2** [Hamza] Warm context with vector search: - - [ ] Recent decisions and their contexts from current plan tree - - [ ] Indexed embeddings from project files - - [ ] Vector search results for quick retrieval - - [ ] Decision chain that led to current work - - [ ] **G4.3** [Hamza] Cold context for historical decisions: - - [ ] Historical decisions from past plans on this codebase - - [ ] Past refactoring patterns ("last time we did X...") - - [ ] Cross-project learnings (if enabled) - - [ ] Queryable but not in active memory - - [ ] **G4.4** [Hamza] Per-actor context views: - - [ ] Implement `ActorContextView` service - - [ ] Strategist view: architecture docs, READMEs, module boundaries, dependency graphs - - [ ] Executor view: precise code sections for edits, test files - - [ ] Reviewer view: diffs, tests, risk zones, style guides - - [ ] Each actor gets filtered view based on role - - [ ] **G4.5** [Hamza] Implement promotion/demotion algorithms: - - [ ] Analyze current query/task - - [ ] Promote relevant data upward (cold -> warm -> hot) - - [ ] Demote stale data out of hot to keep prompts tight - - [ ] Preserve complete context snapshots for every decision - - [ ] **G4.6** [Rui] Context tier tests: - - [ ] Scenario: Hot context respects file limit - - [ ] Scenario: Warm context includes recent decisions - - [ ] Scenario: Actor receives role-appropriate context view - - [ ] Scenario: Promotion moves relevant cold data to warm - -- [ ] **Stage G5: Cost & Risk Estimation** (Day 35) **[Hamza]** - - [ ] **G5.1** [Hamza] Estimation actor implementation: - - [ ] Create optional `estimation_actor` role in action model - - [ ] Estimation actor runs after Strategize completes, before Execute - - [ ] Input: strategy output, project context - - [ ] Output: cost estimate, risk assessment, time estimate - - [ ] **G5.2** [Hamza] Token/cost estimation: - - [ ] Analyze strategy to estimate number of LLM calls - - [ ] Estimate tokens per call based on context size - - [ ] Map model costs to get dollar estimate - - [ ] Estimate number of steps/subplans - - [ ] Provide confidence interval (min/expected/max) - - [ ] **G5.3** [Hamza] Risk assessment: - - [ ] Analyze strategy for risky operations: - - [ ] Touching auth/security code - - [ ] Database migrations - - [ ] Public API changes - - [ ] Infrastructure changes - - [ ] Calculate risk score (low/medium/high) - - [ ] Identify specific risk factors - - [ ] Estimate likelihood of rollback needed - - [ ] **G5.4** [Hamza] Display estimation before execute: - - [ ] Show estimated cost before user confirms execute - - [ ] Support `--yes` to bypass confirmation in automation contexts - - [ ] Show risk assessment summary - - [ ] Allow user to abort if cost too high - - [ ] **G5.5** [Rui] Estimation tests: - - [ ] Scenario: Estimation actor produces cost estimate - - [ ] Scenario: High-risk strategy flagged appropriately - - [ ] Scenario: User can abort based on estimate - -- [ ] **Stage G6: Full CLI Polish** (Day 35) **[All]** - - [ ] **G6.1** [All] Consistent help text - - [ ] **G6.2** [All] Rich terminal output - - [ ] **G6.3** [All] Progress indicators - - [ ] **G6.4** [All] Error messages with recovery suggestions - -**M7 SUCCESS CRITERIA**: -- [ ] All spec features implemented -- [ ] Full test coverage (>85%) -- [ ] Documentation complete -- [ ] Ready for release - ---- ### Section 10: Async Infrastructure & Later-Stage Quality Work [Various Leads] @@ -2696,22 +2473,22 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Luis]: Add job enqueue hooks for plan execute/apply when async is enabled via config flag (no new CLI flags). - [ ] Code [Luis]: Add cancellation token support and ensure cancellation propagates to tool execution. - [ ] Docs [Luis]: Update `docs/reference/async_architecture.md` with execution flow, job states, and shutdown rules. - - [ ] Tests (Behave) [Rui]: Add `features/async_execution.feature` for async command handling (enqueue, worker pick-up, cancel). - - [ ] Tests (Robot) [Rui]: Add `robot/async_execution.robot` smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/async_execution_bench.py` for worker scheduling overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add `features/async_execution.feature` for async command handling (enqueue, worker pick-up, cancel). + - [ ] Tests (Robot) [Luis]: Add `robot/async_execution.robot` smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/async_execution_bench.py` for worker scheduling overhead. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(async): add async command execution and workers"`. - [ ] **COMMIT (Owner: Luis | Group: 10A.retry) - Commit message: "feat(async): wire retry policies into services"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Integrate retry/circuit breaker policies into service layer operations. - [ ] Code [Luis]: Add retry policy configuration keys (max_attempts, base_delay, max_delay, jitter) to settings. - [ ] Code [Luis]: Ensure retries are only applied to idempotent operations (repository reads, validation calls) and never to applies. - [ ] Docs [Luis]: Document retry policy defaults and override points. - - [ ] Tests (Behave) [Rui]: Add retry/circuit breaker behavior scenarios. - - [ ] Tests (Robot) [Rui]: Add resilience smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/retry_policy_bench.py` for retry overhead. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add retry/circuit breaker behavior scenarios. + - [ ] Tests (Robot) [Luis]: Add resilience smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/retry_policy_bench.py` for retry overhead. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(async): wire retry policies into services"`. **Parallel Group 10B: Selective Quality Review [Brent]** @@ -2719,9 +2496,9 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Docs [Brent]: Create `docs/development/review_playbook.md` with focus areas and skip rules. - [ ] Docs [Brent]: Add priority matrix and review SLA guidance. - [ ] Docs [Brent]: Add checklist templates for architecture review, CLI review, and DB migration review. - - [ ] Tests (Behave) [Rui]: Add scenarios validating review playbook references exist. - - [ ] Tests (Robot) [Rui]: Add docs build smoke test covering the new guide. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/docs_build_bench.py` for docs build baseline. + - [ ] Tests (Behave) [Brent]: Add scenarios validating review playbook references exist. + - [ ] Tests (Robot) [Brent]: Add docs build smoke test covering the new guide. + - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/docs_build_bench.py` for docs build baseline. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Brent]: `git commit -m "docs(qa): add review playbook and priority matrix"`. @@ -2731,9 +2508,9 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Brent]: Add shared edge-case fixtures under `features/fixtures/validation/`. - [ ] Code [Brent]: Add fixtures for malformed tool outputs, missing resources, and validation timeouts. - [ ] Docs [Brent]: Update `docs/development/testing.md` with validation test catalog. - - [ ] Tests (Behave) [Rui]: Add edge-case scenarios for concurrency, conflicts, rollbacks, and timeouts. - - [ ] Tests (Robot) [Rui]: Add integration coverage for edge-case suites. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/validation_edge_bench.py` for edge-case runtime. + - [ ] Tests (Behave) [Brent]: Add edge-case scenarios for concurrency, conflicts, rollbacks, and timeouts. + - [ ] Tests (Robot) [Brent]: Add integration coverage for edge-case suites. + - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/validation_edge_bench.py` for edge-case runtime. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Brent]: `git commit -m "test(validation): add edge case suites"`. @@ -2741,19 +2518,19 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Luis]: Add semantic validation fixtures and error-pattern samples. - [ ] Code [Luis]: Add fixtures for language-porting mismatches and dependency graph violations. - [ ] Docs [Luis]: Document semantic validation coverage expectations. - - [ ] Tests (Behave) [Rui]: Add semantic validation scenarios. - - [ ] Tests (Robot) [Rui]: Add semantic validation integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/semantic_validation_suite_bench.py` for suite runtime. - - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Tests (Behave) [Luis]: Add semantic validation scenarios. + - [ ] Tests (Robot) [Luis]: Add semantic validation integration tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/semantic_validation_suite_bench.py` for suite runtime. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "test(validation): add semantic validation suites"`. - [ ] **COMMIT (Owner: Brent | Group: 10C.performance) - Commit message: "test(perf): add scale test fixtures"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Brent]: Add scale fixtures for 1K/5K/10K file repos in `features/fixtures/scale/`. - [ ] Code [Brent]: Add scriptless fixture generator instructions (documented, no helper scripts). - [ ] Docs [Brent]: Add scale test runbook and environment notes. - - [ ] Tests (Behave) [Rui]: Add scale test scenarios validating thresholds. - - [ ] Tests (Robot) [Rui]: Add large-project Robot tests for performance runs. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/scale_fixture_bench.py` for baseline performance. + - [ ] Tests (Behave) [Brent]: Add scale test scenarios validating thresholds. + - [ ] Tests (Robot) [Brent]: Add large-project Robot tests for performance runs. + - [ ] Tests (ASV) [Brent]: Add `asv/benchmarks/scale_fixture_bench.py` for baseline performance. - [ ] Quality [Brent]: Run `nox` (all default sessions, including benchmark). - [ ] Quality [Brent]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Brent]: `git commit -m "test(perf): add scale test fixtures"`. @@ -2764,77 +2541,84 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is **Target: Throughout project, critical items by Day 14** -**Note**: With automated quality gates in place (Section 0), Brent only reviews security changes after automated scanning. +**Note**: Security tasks focus on runtime protections; quality gates are handled in Section 0. **Parallel Group SEC1: Remove eval() usage [Luis]** - [ ] **COMMIT (Owner: Luis | Group: SEC1.eval) - Commit message: "fix(security): remove eval-based config parsing"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Audit and remove all `eval`/`exec`/`compile` usage from production config paths. - [ ] Docs [Luis]: Add `docs/reference/security_eval.md` with replacement patterns. - - [ ] Tests (Behave) [Rui]: Add `features/security_eval.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add `robot/security_eval.robot` smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_eval_bench.py` for config parsing baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add `features/security_eval.feature` scenarios. + - [ ] Tests (Robot) [Luis]: Add `robot/security_eval.robot` smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/security_eval_bench.py` for config parsing baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "fix(security): remove eval-based config parsing"`. **Parallel Group SEC2: Template Injection Prevention [Luis]** - [ ] **COMMIT (Owner: Luis | Group: SEC2.template) - Commit message: "fix(security): harden template rendering"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Replace unsafe template usage with a sandboxed renderer and strict token set. - [ ] Docs [Luis]: Add `docs/reference/template_security.md` with safe patterns. - - [ ] Tests (Behave) [Rui]: Add `features/security_templates.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add `robot/security_templates.robot` smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_template_bench.py` for render baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add `features/security_templates.feature` scenarios. + - [ ] Tests (Robot) [Luis]: Add `robot/security_templates.robot` smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/security_template_bench.py` for render baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "fix(security): harden template rendering"`. **Parallel Group SEC3: Exception Handling Audit [Luis]** - [ ] **COMMIT (Owner: Luis | Group: SEC3.exceptions) - Commit message: "fix(security): enforce explicit exception handling"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Replace silent exception handling with explicit errors and context propagation. - [ ] Docs [Luis]: Document error propagation standards and logging rules. - - [ ] Tests (Behave) [Rui]: Add `features/security_exceptions.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add exception handling integration smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_exception_bench.py` for error path overhead baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add `features/security_exceptions.feature` scenarios. + - [ ] Tests (Robot) [Luis]: Add exception handling integration smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/security_exception_bench.py` for error path overhead baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "fix(security): enforce explicit exception handling"`. **Parallel Group SEC4: Async Lifecycle Correctness [Luis]** - [ ] **COMMIT (Owner: Luis | Group: SEC4.async) - Commit message: "fix(security): close async resources and leaks"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Close async resources, checkpoint files, and subscription leaks with retention policies. - [ ] Docs [Luis]: Add `docs/reference/async_safety.md` on cleanup rules. - - [ ] Tests (Behave) [Rui]: Add `features/security_async.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add async cleanup integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_async_cleanup_bench.py` for cleanup overhead baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add `features/security_async.feature` scenarios. + - [ ] Tests (Robot) [Luis]: Add async cleanup integration tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/security_async_cleanup_bench.py` for cleanup overhead baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "fix(security): close async resources and leaks"`. **Parallel Group SEC5: Secrets Management [Hamza]** - [ ] **COMMIT (Owner: Hamza | Group: SEC5.secrets) - Commit message: "feat(security): add secrets masking and validation"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Mask credentials in logs, validate required keys, and block secret leakage in outputs. - [ ] Docs [Hamza]: Add `docs/reference/secrets_handling.md`. - - [ ] Tests (Behave) [Rui]: Add `features/security_secrets.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add secrets handling integration smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_secrets_bench.py` for masking overhead baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add `features/security_secrets.feature` scenarios. + - [ ] Tests (Robot) [Hamza]: Add secrets handling integration smoke tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/security_secrets_bench.py` for masking overhead baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(security): add secrets masking and validation"`. **Parallel Group SEC6: Read-Only Enforcement [Luis]** - [ ] **COMMIT (Owner: Luis | Group: SEC6.readonly) - Commit message: "feat(security): enforce read-only actions"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Luis]: Validate read-only actions only use read-only skills at execution time. - [ ] Docs [Luis]: Add `docs/reference/read_only_actions.md`. - - [ ] Tests (Behave) [Rui]: Add `features/security_readonly.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add read-only enforcement integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_readonly_bench.py` for enforcement overhead baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add `features/security_readonly.feature` scenarios. + - [ ] Tests (Robot) [Luis]: Add read-only enforcement integration tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/security_readonly_bench.py` for enforcement overhead baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(security): enforce read-only actions"`. - - [ ] Note: Safety profile enforcement is deferred; see Section 18 POST1. + - [ ] Note: Safety profile enforcement is deferred; see Section 18 POST.safety. **Parallel Group SEC7: Audit Logging [Hamza]** - [ ] **COMMIT (Owner: Hamza | Group: SEC7.audit) - Commit message: "feat(security): add audit logging for apply"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - [ ] Code [Hamza]: Add audit log model, migration, and `agents audit list` CLI command. - [ ] Docs [Hamza]: Add `docs/reference/audit_logging.md`. - - [ ] Tests (Behave) [Rui]: Add `features/security_audit.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add audit logging integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/security_audit_bench.py` for log write overhead baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add `features/security_audit.feature` scenarios. + - [ ] Tests (Robot) [Hamza]: Add audit logging integration tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/security_audit_bench.py` for log write overhead baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(security): add audit logging for apply"`. ### Section 12: Session & Provider Fixes [WORKSTREAM G - Hamza] @@ -2847,10 +2631,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Hamza]: Implement SessionService with create/list/show/delete/export/import/tell operations per spec. - [ ] Code [Hamza]: Implement CLI commands `session create/list/show/delete/export/import/tell` with rich/plain/json output. - [ ] Docs [Hamza]: Add `docs/reference/session_management.md` with CLI examples and output fields. - - [ ] Tests (Behave) [Rui]: Add `features/session_management.feature` scenarios for create/list/show/delete/export/import/tell. - - [ ] Tests (Robot) [Rui]: Add session CLI smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/session_cli_bench.py` for session command overhead. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add `features/session_management.feature` scenarios for create/list/show/delete/export/import/tell. + - [ ] Tests (Robot) [Hamza]: Add session CLI smoke tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/session_cli_bench.py` for session command overhead. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(session): add session model and CLI"`. **Parallel Group SESS2: Memory Persistence [Hamza]** @@ -2858,10 +2643,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Hamza]: Persist MemoryService history keyed by session_id with backend config and retention limits. - [ ] Code [Hamza]: Add `session_messages` table (session_id, role, content, created_at) and indexing for recent retrieval. - [ ] Docs [Hamza]: Document memory backend options, retention policy, and export/import behavior. - - [ ] Tests (Behave) [Rui]: Add `features/memory_persistence.feature` scenarios for save/load/trim. - - [ ] Tests (Robot) [Rui]: Add memory persistence integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/memory_persistence_bench.py` for storage overhead baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add `features/memory_persistence.feature` scenarios for save/load/trim. + - [ ] Tests (Robot) [Hamza]: Add memory persistence integration tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/memory_persistence_bench.py` for storage overhead baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(memory): persist session history"`. **Parallel Group PROV1: Provider Fixes [Luis]** @@ -2869,10 +2655,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Luis]: Remove FakeListLLM fallback, fix auto-debug provider usage, and implement provider auto-detection. - [ ] Code [Luis]: Update settings validation to fail fast when no providers are configured and no mock flag is set. - [ ] Docs [Luis]: Update provider configuration docs and error messages. - - [ ] Tests (Behave) [Rui]: Add `features/provider_fixes.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add provider detection smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/provider_selection_bench.py` for provider resolution baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add `features/provider_fixes.feature` scenarios. + - [ ] Tests (Robot) [Luis]: Add provider detection smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/provider_selection_bench.py` for provider resolution baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "fix(provider): remove FakeListLLM defaults"`. **Parallel Group PROV2: Cost Controls & Fallback [Luis]** @@ -2880,10 +2667,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Luis]: Track tokens/costs, enforce budgets, rate limits, and provider fallback order. - [ ] Code [Luis]: Add cost tracking fields to plan execution metadata and surface in `plan status`. - [ ] Docs [Luis]: Add `docs/reference/cost_controls.md` with config keys and thresholds. - - [ ] Tests (Behave) [Rui]: Add `features/cost_controls.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add cost control integration smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/cost_controls_bench.py` for cost check overhead. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add `features/cost_controls.feature` scenarios. + - [ ] Tests (Robot) [Luis]: Add cost control integration smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/cost_controls_bench.py` for cost check overhead. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(provider): add cost controls and fallback"`. --- @@ -2895,10 +2683,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Hamza]: Implement `version`, `info`, and `diagnostics` commands with rich/plain/json/yaml output parity. - [ ] Code [Hamza]: Add diagnostics checks for config file, database, providers, and filesystem permissions per spec. - [ ] Docs [Hamza]: Update CLI reference with core system commands and sample outputs. - - [ ] Tests (Behave) [Rui]: Add `features/cli_core.feature` scenarios for each command output. - - [ ] Tests (Robot) [Rui]: Add core command smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/cli_core_bench.py` for command runtime baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add `features/cli_core.feature` scenarios for each command output. + - [ ] Tests (Robot) [Hamza]: Add core command smoke tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/cli_core_bench.py` for command runtime baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(cli): add version/info/diagnostics"`. **Parallel Group CLI1: Plan Interaction Commands [Hamza]** @@ -2906,10 +2695,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Hamza]: Implement `plan prompt`, `plan diff`, and `plan artifacts` commands. - [ ] Code [Hamza]: Ensure `plan diff` supports `--format` output and includes validation summary. - [ ] Docs [Hamza]: Update CLI reference with plan interaction commands and output formats. - - [ ] Tests (Behave) [Rui]: Add `features/plan_interaction_cli.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add plan interaction CLI smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_cli_interaction_bench.py` for diff/artifacts runtime. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add `features/plan_interaction_cli.feature` scenarios. + - [ ] Tests (Robot) [Hamza]: Add plan interaction CLI smoke tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/plan_cli_interaction_bench.py` for diff/artifacts runtime. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(cli): add plan prompt/diff/artifacts"`. **Parallel Group CLI2: Configuration Commands [Hamza]** @@ -2917,10 +2707,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Hamza]: Implement `config set/get/list` and `providers list` commands. - [ ] Code [Hamza]: Support `config list` regex filtering and `--filter-values` per spec. - [ ] Docs [Hamza]: Update CLI reference with configuration commands and filtering examples. - - [ ] Tests (Behave) [Rui]: Add `features/config_cli.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add config CLI smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/config_cli_bench.py` for command parsing baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add `features/config_cli.feature` scenarios. + - [ ] Tests (Robot) [Hamza]: Add config CLI smoke tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/config_cli_bench.py` for command parsing baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(cli): add config and provider commands"`. **Parallel Group CLI3: Context Commands [Hamza]** @@ -2928,10 +2719,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Hamza]: Implement `project context set/show` and `actor context set/show` commands. - [ ] Code [Hamza]: Support include/exclude resource and path globs, token limits, and summarize flags per spec. - [ ] Docs [Hamza]: Update CLI reference with context policy usage and examples. - - [ ] Tests (Behave) [Rui]: Add `features/context_cli.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add context CLI smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/context_cli_bench.py` for command runtime baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add `features/context_cli.feature` scenarios. + - [ ] Tests (Robot) [Hamza]: Add context CLI smoke tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/context_cli_bench.py` for command runtime baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(cli): add context policy commands"`. --- @@ -2944,10 +2736,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Luis]: Add `locks` table with owner_id, resource_type, resource_id, acquired_at, expires_at. - [ ] Code [Luis]: Ensure locks are enforced in PlanLifecycleService transitions and SubplanService scheduling. - [ ] Docs [Luis]: Add `docs/reference/concurrency.md` with lock behavior. - - [ ] Tests (Behave) [Rui]: Add `features/concurrency.feature` scenarios for lock contention and expiry. - - [ ] Tests (Robot) [Rui]: Add lock integration smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/concurrency_lock_bench.py` for lock overhead baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add `features/concurrency.feature` scenarios for lock contention and expiry. + - [ ] Tests (Robot) [Luis]: Add lock integration smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/concurrency_lock_bench.py` for lock overhead baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(concurrency): add plan and project locks"`. **Parallel Group CONC2: Resumable Execution [Luis]** @@ -2955,10 +2748,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Luis]: Persist step-level progress and implement `plan resume` with graceful shutdown handling. - [ ] Code [Luis]: Add resume checkpoints tied to decision IDs and sandbox checkpoints. - [ ] Docs [Luis]: Update plan lifecycle docs for resume behavior. - - [ ] Tests (Behave) [Rui]: Add `features/plan_resume.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add resume integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/plan_resume_bench.py` for resume overhead baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add `features/plan_resume.feature` scenarios. + - [ ] Tests (Robot) [Luis]: Add resume integration tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/plan_resume_bench.py` for resume overhead baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(concurrency): add plan resume"`. **Parallel Group CONC3: Garbage Collection [Hamza]** @@ -2966,10 +2760,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Hamza]: Add cleanup for sandboxes, checkpoints, and stale sessions with CLI commands. - [ ] Code [Hamza]: Add retention policy settings for sandbox age, checkpoint count, and session inactivity. - [ ] Docs [Hamza]: Document cleanup commands and retention defaults. - - [ ] Tests (Behave) [Rui]: Add `features/garbage_collection.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add cleanup integration smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/cleanup_bench.py` for cleanup overhead baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add `features/garbage_collection.feature` scenarios. + - [ ] Tests (Robot) [Hamza]: Add cleanup integration smoke tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/cleanup_bench.py` for cleanup overhead baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(ops): add cleanup commands"`. --- @@ -2982,10 +2777,11 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Hamza]: Add index metadata table (resource_id, indexed_at, file_count, token_estimate). - [ ] Code [Hamza]: Enforce max file size and total size limits from project context policy. - [ ] Docs [Hamza]: Add `docs/reference/context_indexing.md`. - - [ ] Tests (Behave) [Rui]: Add `features/context_indexing.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add indexing integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/context_indexing_bench.py` for indexing throughput baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add `features/context_indexing.feature` scenarios. + - [ ] Tests (Robot) [Hamza]: Add indexing integration tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/context_indexing_bench.py` for indexing throughput baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(context): add repo indexing service"`. **Parallel Group CTX2: Embedding Index [Hamza]** @@ -2993,139 +2789,159 @@ By Day 30, the system must be able to (all in LOCAL MODE, server connectivity is - [ ] Code [Hamza]: Add embedding-based search with opt-in flag and fallback to full-text search. - [ ] Code [Hamza]: Add embedding index metadata and cache invalidation on repo updates. - [ ] Docs [Hamza]: Add `docs/reference/embedding_search.md`. - - [ ] Tests (Behave) [Rui]: Add `features/embedding_search.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add embedding search integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/embedding_search_bench.py` for search runtime baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add `features/embedding_search.feature` scenarios. + - [ ] Tests (Robot) [Hamza]: Add embedding search integration tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/embedding_search_bench.py` for search runtime baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(context): add optional embedding search"`. --- ### Section 17: Skill Registry [Days 17-18] -**Parallel Group SKILL1: Skill Catalog [Aditya]** -- [ ] **COMMIT (Owner: Aditya | Group: SKILL1.registry) - Commit message: "feat(skill): add skill registry and CLI"** (Only check after all subitems + `nox` pass + coverage >=97%, then commit) - - [ ] Code [Aditya]: Implement SkillRegistry, auto-registration from actor configs, and `skills list` CLI. - - [ ] Code [Aditya]: Add skill YAML schema loader for `agents skill add` (namespaced name + tool refs). - - [ ] Code [Aditya]: Implement `agents skill show` and `agents skill tools` outputs per spec. - - [ ] Docs [Aditya]: Add `docs/reference/skill_registry.md`. - - [ ] Tests (Behave) [Rui]: Add `features/skill_registry.feature` scenarios. - - [ ] Tests (Robot) [Rui]: Add skill registry integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/skill_registry_bench.py` for registry lookup baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. - - [ ] Commit [Aditya]: `git commit -m "feat(skill): add skill registry and CLI"`. - --- ### Section 18: Deferred Work -The following items are deferred or no longer applicable: +Deferred items remain planned but are not part of the 30-day MVP scope. + +- [ ] **COMMIT (Owner: Hamza | Group: POST.resource) - Commit message: "feat(resource): add virtual resource equivalence tracking"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Hamza]: Add `virtual_resource_links` table mapping virtual resource ULID to physical resource ULIDs with uniqueness constraints. + - [ ] Code [Hamza]: Add `ResourceEquivalenceService` to create/merge virtual resources and update links on content divergence. + - [ ] Code [Hamza]: Add helper to compute equivalence key (hash or name) for auto-linking during resource discovery. + - [ ] Docs [Hamza]: Update `docs/reference/resource_model.md` with physical/virtual equivalence rules and examples. + - [ ] Tests (Behave) [Hamza]: Add scenarios for linking/unlinking physical resources to virtual resources and divergence updates. + - [ ] Tests (Robot) [Hamza]: Add Robot test that creates two identical physical resources and verifies a shared virtual resource. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/virtual_resource_bench.py` for equivalence update overhead. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(resource): add virtual resource equivalence tracking"`. - [ ] **COMMIT (Owner: Luis | Group: POST.server) - Commit message: "feat(client): add server http client"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Luis]: Add HTTP client with health check, version negotiation, and OpenAPI codegen integration. + - [ ] Code [Luis]: Add config keys for server base URL, API token, and TLS verification; wire into Settings. + - [ ] Code [Luis]: Map server error responses into domain errors with retry hints. - [ ] Docs [Luis]: Add `docs/reference/server_client_http.md` with configuration and connection errors. - - [ ] Tests (Behave) [Rui]: Add scenarios for connection errors and version mismatch handling. - - [ ] Tests (Robot) [Rui]: Add mock-server connection tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/server_http_client_bench.py` for connection overhead baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add scenarios for connection errors and version mismatch handling. + - [ ] Tests (Robot) [Luis]: Add mock-server connection tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/server_http_client_bench.py` for connection overhead baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(client): add server http client"`. - [ ] **COMMIT (Owner: Luis | Group: POST.server) - Commit message: "feat(client): add plan sync and remote execution"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Luis]: Sync actions, request remote plan execution/apply/status, and reconcile remote plan IDs. + - [ ] Code [Luis]: Add conflict resolution policy (local wins vs server wins) with explicit CLI errors on ambiguity. - [ ] Docs [Luis]: Document sync semantics and conflict handling in `docs/reference/server_sync.md`. - - [ ] Tests (Behave) [Rui]: Add scenarios for sync conflicts and retry behavior. - - [ ] Tests (Robot) [Rui]: Add mock-server sync tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/server_sync_bench.py` for sync throughput baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add scenarios for sync conflicts and retry behavior. + - [ ] Tests (Robot) [Luis]: Add mock-server sync tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/server_sync_bench.py` for sync throughput baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(client): add plan sync and remote execution"`. - [ ] **COMMIT (Owner: Luis | Group: POST.server) - Commit message: "feat(client): add websocket updates"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Luis]: Add WebSocket client for plan updates with reconnect/backoff policy. + - [ ] Code [Luis]: Define event schema mapping for plan status/progress/log stream updates. - [ ] Docs [Luis]: Add `docs/reference/server_websocket.md` with event types and reconnect rules. - - [ ] Tests (Behave) [Rui]: Add scenarios for reconnect and event ordering. - - [ ] Tests (Robot) [Rui]: Add WebSocket mock tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/server_ws_bench.py` for message handling baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Luis]: Add scenarios for reconnect and event ordering. + - [ ] Tests (Robot) [Luis]: Add WebSocket mock tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/server_ws_bench.py` for message handling baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Luis]: `git commit -m "feat(client): add websocket updates"`. - [ ] **COMMIT (Owner: Hamza | Group: POST.server) - Commit message: "feat(client): add remote project support"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) - [ ] Code [Hamza]: Add remote resource selection and server execution request wiring. + - [ ] Code [Hamza]: Add project-name resolution rules for remote namespaces and server aliases. - [ ] Docs [Hamza]: Add `docs/reference/server_remote_projects.md` with project selection semantics. - - [ ] Tests (Behave) [Rui]: Add scenarios for remote project selection errors. - - [ ] Tests (Robot) [Rui]: Add remote execution mock tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/server_remote_project_bench.py` for request overhead baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. + - [ ] Tests (Behave) [Hamza]: Add scenarios for remote project selection errors. + - [ ] Tests (Robot) [Hamza]: Add remote execution mock tests. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/server_remote_project_bench.py` for request overhead baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. - [ ] Commit [Hamza]: `git commit -m "feat(client): add remote project support"`. -- [ ] **COMMIT (Owner: TBD | Group: POST.repl) - Commit message: "feat(cli): add interactive repl"** - - [ ] Code [TBD]: Implement REPL command loop with history and completion. - - [ ] Code [TBD]: Add persistent history file under `~/.cleveragents/history` with opt-out flag. - - [ ] Docs [TBD]: Add REPL usage guide. - - [ ] Tests (Behave) [Rui]: Add REPL behavior scenarios. - - [ ] Tests (Robot) [Rui]: Add REPL smoke tests. +- [ ] **COMMIT (Owner: Rui | Group: POST.repl) - Commit message: "feat(cli): add interactive repl"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Rui]: Implement `agents repl` command that dispatches to existing CLI commands with shared config handling. + - [ ] Code [Rui]: Add history support with opt-out (`--no-history`) and default path `~/.cleveragents/history`. + - [ ] Code [Rui]: Add tab-completion for top-level commands and last command repetition (`!!`). + - [ ] Docs [Rui]: Add REPL usage guide with supported commands and exit behavior. + - [ ] Tests (Behave) [Rui]: Add REPL behavior scenarios (history on/off, unknown command, exit). + - [ ] Tests (Robot) [Rui]: Add REPL smoke tests for command dispatch. - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/repl_bench.py` for REPL startup baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. - - [ ] Commit [TBD]: `git commit -m "feat(cli): add interactive repl"`. + - [ ] Quality [Rui]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Rui]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Rui]: `git commit -m "feat(cli): add interactive repl"`. -- [ ] **COMMIT (Owner: TBD | Group: POST.auth) - Commit message: "feat(cli): add auth and team commands"** - - [ ] Code [TBD]: Add auth/team CLI commands (requires server connectivity) with stubbed responses. - - [ ] Code [TBD]: Add config keys for auth token storage and team context. - - [ ] Docs [TBD]: Document auth/team workflows. - - [ ] Tests (Behave) [Rui]: Add auth/team CLI scenarios. - - [ ] Tests (Robot) [Rui]: Add auth/team integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/auth_cli_bench.py` for auth command baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. - - [ ] Commit [TBD]: `git commit -m "feat(cli): add auth and team commands"`. +- [ ] **COMMIT (Owner: Luis | Group: POST.auth) - Commit message: "feat(cli): add auth and team commands"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add `agents auth login/logout/status` and `agents team list/use` commands with stubbed responses when server is disabled. + - [ ] Code [Luis]: Add config keys for auth token storage, active team, and default namespace (client-only stubs). + - [ ] Code [Luis]: Wire stubbed commands to `AuthClient` and `ServerClient` interfaces (raise NotImplementedError when no server). + - [ ] Docs [Luis]: Document auth/team workflows and local-only stub behavior. + - [ ] Tests (Behave) [Luis]: Add auth/team CLI scenarios (stubbed responses, missing server errors). + - [ ] Tests (Robot) [Luis]: Add auth/team integration smoke tests. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/auth_cli_bench.py` for auth command baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(cli): add auth and team commands"`. -- [ ] **COMMIT (Owner: TBD | Group: POST.tui) - Commit message: "feat(ui): add TUI/Web interface"** - - [ ] Code [TBD]: Implement TUI/Web interfaces (client-only) with plan status, logs, and diff views. - - [ ] Code [TBD]: Add UI routing stub and data provider interface (local-only). - - [ ] Docs [TBD]: Add UI usage guide. - - [ ] Tests (Behave) [Rui]: Add UI behavior scenarios. - - [ ] Tests (Robot) [Rui]: Add UI smoke tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/ui_render_bench.py` for UI render baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. - - [ ] Commit [TBD]: `git commit -m "feat(ui): add TUI/Web interface"`. +- [ ] **COMMIT (Owner: Jeff | Group: POST.tui) - Commit message: "feat(ui): add TUI/Web interface"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Jeff]: Define UI data-provider interface (plans, sessions, validations, diffs, logs) backed by local services. + - [ ] Code [Jeff]: Implement minimal TUI with plan list, plan detail, diff viewer, and validation summary panes. + - [ ] Code [Jeff]: Add Web UI stub that serves the same data via local-only routes (read-only by default). + - [ ] Docs [Jeff]: Add UI usage guide with navigation and data-refresh behavior. + - [ ] Tests (Behave) [Jeff]: Add UI behavior scenarios (list, detail, diff, refresh). + - [ ] Tests (Robot) [Jeff]: Add UI smoke tests for route loading and TUI navigation. + - [ ] Tests (ASV) [Jeff]: Add `asv/benchmarks/ui_render_bench.py` for UI render baseline. + - [ ] Quality [Jeff]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Jeff]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Jeff]: `git commit -m "feat(ui): add TUI/Web interface"`. -- [ ] **COMMIT (Owner: TBD | Group: POST.dbresources) - Commit message: "feat(resource): add database resources"** - - [ ] Code [TBD]: Add database resource types and sandbox strategy (transaction wrapper). - - [ ] Code [TBD]: Add resource type schema with connection parameters and auth handling. - - [ ] Docs [TBD]: Document database resource configuration. - - [ ] Tests (Behave) [Rui]: Add database resource scenarios. - - [ ] Tests (Robot) [Rui]: Add database resource integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/db_resource_bench.py` for resource registration baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. - - [ ] Commit [TBD]: `git commit -m "feat(resource): add database resources"`. +- [ ] **COMMIT (Owner: Hamza | Group: POST.dbresources) - Commit message: "feat(resource): add database resources"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Hamza]: Add database resource types (postgres, mysql, sqlite, duckdb) with connection args and auth handling. + - [ ] Code [Hamza]: Implement sandbox strategy using transaction wrappers and read-only toggles. + - [ ] Docs [Hamza]: Document database resource configuration and supported auth options. + - [ ] Tests (Behave) [Hamza]: Add database resource scenarios (connection validation, read-only enforcement). + - [ ] Tests (Robot) [Hamza]: Add database resource integration tests (local sqlite/duckdb only). + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/db_resource_bench.py` for resource registration baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(resource): add database resources"`. -- [ ] **COMMIT (Owner: TBD | Group: POST.cloud) - Commit message: "feat(resource): add cloud infrastructure resources"** - - [ ] Code [TBD]: Add cloud resource types and sandbox strategies (stubbed local-only). - - [ ] Code [TBD]: Add resource type schema with provider-specific credential fields. - - [ ] Docs [TBD]: Document cloud resource configuration. - - [ ] Tests (Behave) [Rui]: Add cloud resource scenarios. - - [ ] Tests (Robot) [Rui]: Add cloud resource integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/cloud_resource_bench.py` for resource registration baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. - - [ ] Commit [TBD]: `git commit -m "feat(resource): add cloud infrastructure resources"`. +- [ ] **COMMIT (Owner: Hamza | Group: POST.cloud) - Commit message: "feat(resource): add cloud infrastructure resources"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Hamza]: Add cloud resource types (aws, gcp, azure) with credential fields and region/tenant metadata. + - [ ] Code [Hamza]: Add stubbed sandbox strategies that validate configuration and return NotImplementedError for execution. + - [ ] Docs [Hamza]: Document cloud resource configuration and local-only stub behavior. + - [ ] Tests (Behave) [Hamza]: Add cloud resource scenarios (schema validation, stub errors). + - [ ] Tests (Robot) [Hamza]: Add cloud resource integration tests with stubbed responses. + - [ ] Tests (ASV) [Hamza]: Add `asv/benchmarks/cloud_resource_bench.py` for resource registration baseline. + - [ ] Quality [Hamza]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Hamza]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Hamza]: `git commit -m "feat(resource): add cloud infrastructure resources"`. -- [ ] **COMMIT (Owner: TBD | Group: POST.permissions) - Commit message: "feat(security): add permission system"** - - [ ] Code [TBD]: Implement namespace/project/plan/skill permission enforcement (requires server). - - [ ] Code [TBD]: Add permission model with role bindings and default deny rules. - - [ ] Docs [TBD]: Document permission model and roles. - - [ ] Tests (Behave) [Rui]: Add permission scenarios. - - [ ] Tests (Robot) [Rui]: Add permission integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/permission_check_bench.py` for enforcement baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. - - [ ] Commit [TBD]: `git commit -m "feat(security): add permission system"`. +- [ ] **COMMIT (Owner: Luis | Group: POST.permissions) - Commit message: "feat(security): add permission system"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Implement namespace/project/plan/skill permission model (role bindings, default deny, allow overrides). + - [ ] Code [Luis]: Add enforcement hooks at CLI/service boundaries (server-only; local mode returns permissive defaults). + - [ ] Docs [Luis]: Document permission model, role matrix, and server-only behavior. + - [ ] Tests (Behave) [Luis]: Add permission scenarios (allow/deny, missing role, server disabled). + - [ ] Tests (Robot) [Luis]: Add permission integration tests with stubbed server client. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/permission_check_bench.py` for enforcement baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(security): add permission system"`. -- [ ] **COMMIT (Owner: TBD | Group: POST.safety) - Commit message: "feat(security): add safety profile enforcement"** - - [ ] Code [TBD]: Add SafetyProfile model, CLI flags, and execution enforcement. - - [ ] Code [TBD]: Add safety profile resolution order (plan > project > global). - - [ ] Docs [TBD]: Document safety profile options and defaults. - - [ ] Tests (Behave) [Rui]: Add safety profile enforcement scenarios. - - [ ] Tests (Robot) [Rui]: Add safety profile integration tests. - - [ ] Tests (ASV) [Rui]: Add `asv/benchmarks/safety_profile_bench.py` for enforcement baseline. - - [ ] Quality [Brent]: Run `nox` and verify coverage >=97%. - - [ ] Commit [TBD]: `git commit -m "feat(security): add safety profile enforcement"`. +- [ ] **COMMIT (Owner: Luis | Group: POST.safety) - Commit message: "feat(security): add safety profile enforcement"** (COMMIT TASK: only check after every subtask below is complete, `nox` and coverage succeed, and the commit is created) + - [ ] Code [Luis]: Add SafetyProfile model, CLI flags, and execution enforcement hooks (server-only for now). + - [ ] Code [Luis]: Add safety profile resolution order (plan > project > global) with defaults. + - [ ] Docs [Luis]: Document safety profile options, defaults, and server-only behavior. + - [ ] Tests (Behave) [Luis]: Add safety profile enforcement scenarios (deny/allow paths). + - [ ] Tests (Robot) [Luis]: Add safety profile integration tests with stubbed server client. + - [ ] Tests (ASV) [Luis]: Add `asv/benchmarks/safety_profile_bench.py` for enforcement baseline. + - [ ] Quality [Luis]: Run `nox` (all default sessions, including benchmark). + - [ ] Quality [Luis]: Verify coverage >=97% via `nox -s coverage_report`. + - [ ] Commit [Luis]: `git commit -m "feat(security): add safety profile enforcement"`. --- @@ -3133,81 +2949,81 @@ The following items are deferred or no longer applicable: ### TEAM ROLES AND ASSIGNMENTS -| Developer | Role | Primary Focus Areas | Notes | -| -------------- | --------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- | -| **Jeff** | CTO/Lead Architect | Critical path items, architectural decisions, complex integrations | Fastest, most expert developer - handles blocking issues | -| **Luis** | Senior Python Architect | Domain models, persistence, algorithms, state machines | Good architecture but can be pedantic - needs clear requirements | -| **Aditya** | Domain Expert (Agents/LLMs) | Actor YAML configs, hierarchical actors, skill execution | Understands topic best but code may need cleanup | -| **Hamza** | Python/RDF Expert | Resources, sandbox, database, general Python | Well-rounded, no agent experience - assign infrastructure | -| **Rui** | Fast Developer | Testing (Behave/Robot), simpler implementations | New to Python - assign testing and straightforward tasks | -| **Brent** | Quality Specialist | Code review, linting, type checking, documentation | Slow but detail-oriented - low contention independent work | -| **Mike/Brian** | Sysadmins | Deployment, infrastructure setup | Minimal coding tasks | +| Developer | Role | Primary Focus Areas | Notes | +|-----------|------|---------------------|-------| +| **Jeff** | CTO/Lead Architect | Critical path items, architectural decisions, complex integrations | Fastest, most expert developer - handles blocking issues | +| **Luis** | Senior Python Architect | Domain models, persistence, algorithms, state machines | Good architecture but can be pedantic - needs clear requirements | +| **Aditya** | Domain Expert (Agents/LLMs) | Actor YAML configs, hierarchical actors, skill execution | Understands topic best but code may need cleanup | +| **Hamza** | Python/RDF Expert | Resources, sandbox, database, general Python | Well-rounded, no agent experience - assign infrastructure | +| **Rui** | Fast Developer | Testing (Behave/Robot), simpler implementations | New to Python - assign testing and straightforward tasks | +| **Brent** | Quality Specialist | Code review, linting, type checking, documentation | Slow but detail-oriented - low contention independent work | +| **Mike/Brian** | Sysadmins | Deployment, infrastructure setup | Minimal coding tasks | ### Week 1 (Days 1-7) - MVP Target (Source Code Only) | Day | Morning Focus | Owner | Afternoon Focus | Owner | |-----|---------------|-------|-----------------|-------| | 1 | A5.alpha + A5.action_arguments DB migrations + ORM models | Jeff + Luis | B1.core Project/Resource models | Hamza | -| 2 | A5.gamma repos/services + A5.tests + B3.cleanup (legacy project CLI) | Jeff | B2.persistence/B2.service + B3.cli Resource CLI | Hamza + Rui (tests) | -| 3 | B4.sandbox git_worktree | Jeff + Hamza | B4.sandbox manager + tests | Luis + Rui | +| 2 | A5.gamma repos/services + A5.tests + B3.cleanup (legacy project CLI) | Jeff + Rui (tests) | B2.persistence/B2.service + B3.cli Resource CLI | Hamza + Rui (tests) | +| 3 | B4.sandbox git_worktree | Jeff + Hamza | B4.sandbox manager + tests | Luis + Rui (tests) | | 4 | C1.schema/C1.examples Actor YAML | Aditya | C2.loader/C2.compiler + C2.legacy v2 removal | Aditya + Jeff | | 5 | C3.protocol/C3.context/C3.inline Skill framework | Jeff | C4.file/C4.search Built-in skills | Luis + Jeff | | 6 | C7.mcp MCP Adapter | Aditya + Jeff | C4.git + C5.model/C5.router Change tracking | Luis + Jeff | -| 7 | C5.diff Diff review artifacts | Luis | C6.pipeline/C6.gating Validation pipeline | Luis + Rui | +| 7 | C5.diff Diff review artifacts | Luis | C6.pipeline/C6.gating Validation pipeline | Luis + Rui (tests) | ### Week 2 (Days 8-14) - M3 Complete + Plan-Actor Integration | Day | Focus | Owner | Deliverable | |-----|-------|-------|-------------| -| 8 | C6.1-C6.8 Plan-Actor Integration | Jeff + Aditya | Full execute phase working | -| 9 | C7.1-C7.6 Apply Phase + Diff Review | Jeff + Luis | Apply with review gates | -| 10 | D1.1-D1.8 Decision Model | Hamza + Jeff | Decision recording foundation | -| 11 | D2.1-D2.6 Decision Recording | Jeff + Hamza | Decisions captured in Strategize | -| 12 | E1.1-E1.6 Subplan Model | Luis | Subplan spawning design | -| 13 | E2.1-E2.5 Subplan Execution | Jeff + Luis | Sequential subplan execution | -| 14 | End-to-end integration testing | All + Rui | M3 milestone verified | +| 8 | C8.providers + C9.execute Plan-Actor integration | Jeff + Aditya (+Luis support) | Execute phase + providers ready | +| 9 | C9.apply Apply Phase + Review | Jeff + Luis | Apply flow ready with review gates | +| 10 | D1.domain Decision Model | Hamza (+Jeff review) | Decision model committed | +| 11 | D2.service Decision Recording | Hamza (+Jeff review) | Decision recording committed | +| 12 | E1.domain Subplan Model | Luis (+Jeff review) | Subplan domain committed | +| 13 | E2.service/E2.actor Subplan spawning | Jeff + Aditya (+Luis support) | Subplan spawn committed | +| 14 | End-to-end integration testing | All + Rui (tests) + Brent (QA) | M3 milestone verified | ### Week 3 (Days 15-21) - M4 Target (Decision Tree + Correction) | Day | Focus | Owner | Deliverable | |-----|-------|-------|-------------| -| 15 | D3.1-D3.6 Decision Tree Storage | Hamza | Decision persistence | -| 16 | D4.1-D4.5 Decision CLI Commands | Hamza + Rui | `agents [--data-dir PATH] [--config-path PATH] plan tree`, `agents [--data-dir PATH] [--config-path PATH] plan explain` | -| 17 | D5.1-D5.8 Decision Correction | Jeff | `agents [--data-dir PATH] [--config-path PATH] plan correct` implementation | -| 18 | D5.9-D5.12 Replay Mechanism | Jeff + Luis | Downstream recomputation | -| 19 | E3.1-E3.5 Parallel Subplan Execution | Luis | Concurrent subplans | -| 20 | E4.1-E4.5 Result Merging | Jeff + Luis | Git-style merge for subplans | -| 21 | M4 integration testing | All | Decision correction working | +| 15 | D5.db/D5.repo Decision persistence | Hamza (+Jeff review) | Decision storage wired | +| 16 | D3.cli Decision viewing | Hamza (+Jeff review) | `agents [--data-dir PATH] [--config-path PATH] plan tree`, `agents [--data-dir PATH] [--config-path PATH] plan explain` | +| 17 | D4.revert Decision correction (revert) | Jeff (+Luis support) | `agents [--data-dir PATH] [--config-path PATH] plan correct` revert flow | +| 18 | D4.append + D5.di Decision wiring | Jeff + Hamza (+Luis support) | Append correction + service wiring | +| 19 | E3.exec Parallel Subplan Execution | Luis (+Jeff review) | Concurrent subplans | +| 20 | E4.merge Result Merging | Jeff (+Luis support) | Git-style merge for subplans | +| 21 | M4 integration testing | All + Rui (tests) + Brent (QA) | Decision correction working | ### Week 4 (Days 22-30) - M6 Target (Large Project Autonomy - LOCAL MODE ONLY) | Day | Focus | Owner | Deliverable | |-----|-------|-------|-------------| -| 22-23 | F1.1-F1.8 Context Indexing | Hamza | Large codebase indexing | -| 24-25 | F2.1-F2.6 Hot/Warm/Cold Context | Jeff + Hamza | Three-tier memory | -| 26-27 | Deep Subplan Hierarchies (5+ levels) | Jeff + Luis | Autonomous decomposition | -| 28-29 | F0.1-F0.5 Server Client Interface Stubs + Large Project Tests | Luis + Rui | Client stubs (NOT server impl), 10K file tests | -| 30 | M6 integration testing | All | Large project autonomy verified (Server connectivity DEFERRED) | +| 22-23 | CTX1.index Context indexing + G3.semantic | Hamza + Luis | Large codebase indexing + semantic validation | +| 24-25 | G4.context Hot/Warm/Cold tiers + G2.checkpoint | Hamza + Luis | Three-tier memory + checkpointing | +| 26-27 | G1.decompose + G5.estimate | Jeff + Hamza | Autonomous decomposition + estimation | +| 28 | F0.stubs | Luis | Client stubs (NOT server impl) | +| 29 | M6 perf triage + large project tests | All + Rui (tests) + Brent (QA) | 10K file perf target | +| 30 | M6 integration testing | All + Rui (tests) + Brent (QA) | Large project autonomy verified (Server connectivity DEFERRED) | > **Note**: Server connectivity (F1-F4) is DEFERRED beyond Day 30. Days 26-29 focus on client **stubs only** and large project testing. The server is a separate project. ### Week 5 (Days 31-35) - M7 Target (Server Connectivity - Client Side Only) | Day | Focus | Owner | Deliverable | |-----|-------|-------|-------------| -| 31-32 | F1.1-F1.4 Server Client Infrastructure | Luis | HTTP client for server communication | -| 33 | F2.1-F2.6 Plan Sync Client | Luis | Client can sync plans to server | -| 34 | F3.1-F3.3 WebSocket Client | Luis | Client receives real-time updates | -| 35 | F4.1-F4.3 Remote Project Support | Hamza | Client can request server execution | +| 31-32 | F1.client Server client infrastructure | Luis (+Jeff review) | HTTP client for server communication | +| 33 | F2.sync Plan sync client | Luis (+Jeff review) | Client can sync plans to server | +| 34 | F3.ws WebSocket client | Luis (+Jeff review) | Client receives real-time updates | +| 35 | F4.remote Remote project support | Hamza (+Jeff review) | Client can request server execution | ### Week 6 (Days 36-40) - M8 Target (Full Feature Set + Polish) | Day | Focus | Owner | Deliverable | |-----|-------|-------|-------------| -| 36 | Automation level refinement | Jeff + Luis | G1 automation enhancements | -| 37 | Cost estimation actors | Aditya | G5 estimation working | -| 38 | Error recovery mechanisms | Jeff | G2 checkpointing + rollback | -| 39 | Performance optimization | Luis + Jeff | Benchmarks passing | -| 40 | Final integration + documentation | All | Release candidate ready | +| 36 | A6.* automation level refinements | Jeff + Luis | Automation modes stabilized | +| 37 | G5.estimate cost/risk estimation | Hamza (+Jeff review) | Estimation working | +| 38 | G2.checkpoint rollback | Luis (+Jeff review) | Checkpointing + rollback | +| 39 | G1.decompose + G3.semantic performance tuning | Jeff + Luis | Benchmarks passing | +| 40 | Final integration + documentation | All + Rui (tests) + Brent (QA) | Release candidate ready | ### Continuous Tasks (Throughout) **Brent (Quality - Independent, Low Contention)**: - - Review all PRs within 4 hours of submission - Run `nox -s typecheck` on all branches before merge - Run `nox -s lint` and ensure 0 warnings @@ -3216,7 +3032,6 @@ The following items are deferred or no longer applicable: - Security audit: no eval(), no template injection, no secrets in code **Rui (Testing - Parallel with Feature Work)**: - - Write Behave scenarios for each feature (before implementation starts) - Write Robot integration tests for each milestone - Run full test suite daily @@ -3250,11 +3065,11 @@ Day 22-30: CTX/G + F0 (Context + Large Project + Stubs) ─┘ | Risk | Mitigation | Owner | |------|------------|-------| -| Git worktree complexity | Jeff handles sandbox implementation | Jeff | -| Multi-file generation reliability | Extensive testing, fallback mechanisms | Luis + Rui | -| Decision tree correction bugs | Jeff reviews all correction logic | Jeff | -| Large codebase performance | Early profiling, lazy loading | Luis + Hamza | -| Server mode stability | Incremental rollout, feature flags | Jeff + Luis | +| Git worktree complexity | Use B4.sandbox git_worktree with pre-commit verification and isolation tests | Jeff | +| Multi-file generation reliability | Validate C9.execute/C9.apply flows with diff review artifacts and Robot E2E | Luis + Rui | +| Decision tree correction bugs | Jeff reviews D4 correction + checkpointing; add revert/append Behave coverage | Jeff | +| Large codebase performance | Profile CTX1/CTX2 indexing + hot/warm/cold tiers; enforce bounded memory tests | Luis + Hamza | +| Server connectivity stubs stability | Keep client-only stubs isolated; gate with feature flags and contract tests | Jeff + Luis | ### Definition of Done (Each Task) @@ -3274,7 +3089,6 @@ Day 22-30: CTX/G + F0 (Context + Large Project + Stubs) ─┘ ### M1: MVP (Day 7) - Minimally Usable for Source Code **End-to-end verification command sequence:** - ```bash # 1. Create an action cat > /tmp/test_action.yaml <85% +- Plan and Action records persist to SQLite database. +- Phase transitions (ACTION → STRATEGIZE → EXECUTE → APPLY → APPLIED) work correctly. +- Git worktree sandbox creates isolated working directory. +- Changes in sandbox do not affect original until Apply. +- At least 3 automation levels work (manual mode minimum). +- Error handling produces actionable messages. +- Test coverage remains >=97%. ### M3: Full Plan Lifecycle with Actors (Day 14) **End-to-end verification:** - ```bash # Create actor YAML file cat > my_actor.yaml < # Should ``` **Technical Criteria:** -- [ ] Actor YAML files parse and validate correctly -- [ ] Actors compile to LangGraph StateGraphs -- [ ] Inline skill code executes in sandboxed environment -- [ ] Built-in file skills (read/write/edit/delete) work -- [ ] ChangeSet built from skill invocations (not parsed from output) -- [ ] Validation pipeline runs (syntax check, lint, tests) -- [ ] Multi-file generation produces correct ChangeSet -- [ ] MCP skill adapter can connect to external servers (basic) +- Actor YAML files parse and validate correctly. +- Actors compile to LangGraph StateGraphs. +- Inline skill code executes in sandboxed environment. +- Built-in file skills (read/write/edit/delete) work. +- ChangeSet built from skill invocations (not parsed from output). +- Validation pipeline runs (syntax check, lint, tests). +- Multi-file generation produces correct ChangeSet. +- MCP skill adapter can connect to external servers (basic). ### M4: Decision Tree & Correction (Day 21) **End-to-end verification:** - ```bash # Execute a plan to generate decisions agents [--data-dir PATH] [--config-path PATH] plan use local/complex-action local/large-project @@ -3408,22 +3220,21 @@ agents [--data-dir PATH] [--config-path PATH] plan tree ``` **Technical Criteria:** -- [ ] Decisions recorded during Strategize with full context snapshot -- [ ] Decision tree persists to database -- [ ] `agents [--data-dir PATH] [--config-path PATH] plan tree` displays ASCII tree correctly -- [ ] `agents [--data-dir PATH] [--config-path PATH] plan explain` shows all decision details -- [ ] Correction in revert mode: - - [ ] Archives old decisions - - [ ] Rolls back sandbox to checkpoint - - [ ] Re-executes from decision point - - [ ] Generates new downstream decisions -- [ ] Correction in append mode creates fix subplan -- [ ] History preserved for comparison +- Decisions recorded during Strategize with full context snapshot. +- Decision tree persists to database. +- `agents [--data-dir PATH] [--config-path PATH] plan tree` displays ASCII tree correctly. +- `agents [--data-dir PATH] [--config-path PATH] plan explain` shows all decision details. +- Correction in revert mode: + - Archives old decisions. + - Rolls back sandbox to checkpoint. + - Re-executes from decision point. + - Generates new downstream decisions. +- Correction in append mode creates fix subplan. +- History preserved for comparison. ### M5: Subplans & Parallel Execution (Day 25) **End-to-end verification:** - ```bash # Execute plan that spawns multiple subplans agents [--data-dir PATH] [--config-path PATH] plan use local/refactor-action local/monorepo @@ -3445,20 +3256,19 @@ agents [--data-dir PATH] [--config-path PATH] plan diff # Shows merge ``` **Technical Criteria:** -- [ ] SUBPLAN_SPAWN decisions created during Strategize -- [ ] Subplans actually spawned during Execute -- [ ] Sequential subplan execution works (one at a time) -- [ ] Parallel subplan execution works (with max_parallel limit) -- [ ] Each subplan has isolated sandbox -- [ ] Three-way merge combines non-conflicting changes -- [ ] Merge conflicts detected and marked -- [ ] Parent plan tracks all subplan statuses -- [ ] A plan with 10+ subplans completes successfully +- SUBPLAN_SPAWN decisions created during Strategize. +- Subplans actually spawned during Execute. +- Sequential subplan execution works (one at a time). +- Parallel subplan execution works (with max_parallel limit). +- Each subplan has isolated sandbox. +- Three-way merge combines non-conflicting changes. +- Merge conflicts detected and marked. +- Parent plan tracks all subplan statuses. +- A plan with 10+ subplans completes successfully. ### M6: Large Project Handling (Day 30) **End-to-end verification:** - ```bash # Index a large project (10,000+ files) agents [--data-dir PATH] [--config-path PATH] project create local/large-project @@ -3507,13 +3317,20 @@ agents [--data-dir PATH] [--config-path PATH] plan apply ``` **Technical Criteria:** -- [ ] Projects with 10,000+ files index without timeout -- [ ] Context window management works (hot/warm/cold tiers) -- [ ] Hierarchical decomposition creates 4+ levels of subplans -- [ ] Decision correction at any level recomputes only affected subtree -- [ ] Parallel execution scales to 10+ concurrent subplans -- [ ] Memory usage stays bounded (lazy context loading) -- [ ] A realistic porting task (500 file Python → TypeScript) completes autonomously +- Projects with 10,000+ files index without timeout. +- Context window management works (hot/warm/cold tiers). +- Hierarchical decomposition creates 4+ levels of subplans. +- Decision correction at any level recomputes only affected subtree. +- Parallel execution scales to 10+ concurrent subplans. +- Memory usage stays bounded (lazy context loading). +- A realistic porting task (500 file Python → TypeScript) completes autonomously. + +**M6 SUCCESS CRITERIA** (Day 30): +- 10,000+ file project indexes with bounded memory and hot/warm/cold tiering. +- Hierarchical decomposition reaches 4+ levels with correction limited to affected subtree. +- Parallel execution scales to 10+ subplans with merge and conflict handling. +- Autonomous porting task completes with validation and review gates. +- `nox` passes with coverage >=97% including large-project suites. --- @@ -3639,18 +3456,12 @@ DAY 30: M6 TARGET ⊕ **Server connectivity (WORKSTREAM F) is deferred beyond the 30-day timeline. The server is a separate project—this implementation covers the client only.** The CleverAgents executable (`agents`) is purely a **client application** that can: - 1. Run in stand-alone local-only mode (no server required) 2. Connect to an independently developed CleverAgents server for multi-user/collaborative features -During Days 1-30, the following client stub infrastructure should be created: -- [ ] Server client connection command (`agents [--data-dir PATH] [--config-path PATH] connect ` - stub) -- [ ] Abstract interfaces for client-to-server communication -- [ ] Resource abstraction that can detect local vs remote resources -- [ ] Placeholder client methods that return "Server connectivity not yet implemented" +During Days 1-30, client stub infrastructure is delivered via Section 9 (F0.stubs), covering the connect command, client interfaces, local/remote detection, and NotImplementedError stubs. **What is NOT needed by Day 30:** - - Server implementation (the server is a separate project) - Full client-server API implementation - WebSocket client implementation -- 2.52.0 From 62e6243285effe716dae608eb9f6f95dafe6c5b1 Mon Sep 17 00:00:00 2001 From: khyari hamza Date: Thu, 12 Feb 2026 15:48:17 +0100 Subject: [PATCH 07/11] chore: untrack working notes files --- B1_SUMMARY.md | 284 --------------- CURRENT_TASK.md | 114 ------ HAMZA_PROGRESS.md | 213 ----------- IMPLEMENTATION_PLAYBOOK.md | 718 ------------------------------------- notes.md | 12 - 5 files changed, 1341 deletions(-) delete mode 100644 B1_SUMMARY.md delete mode 100644 CURRENT_TASK.md delete mode 100644 HAMZA_PROGRESS.md delete mode 100644 IMPLEMENTATION_PLAYBOOK.md delete mode 100644 notes.md diff --git a/B1_SUMMARY.md b/B1_SUMMARY.md deleted file mode 100644 index d85faec46..000000000 --- a/B1_SUMMARY.md +++ /dev/null @@ -1,284 +0,0 @@ -# B1 - Project Data Model: Implementation Summary - -> **Stage:** B1 (Phase 1: Foundation) -> **Status:** COMPLETE -> **Completed:** 2026-02-11 -> **Tasks:** 24/24 (6 top-level, 18 subtasks) - ---- - -## PR Summary - -### `feat(domain): add project data model foundation (B1)` - -#### Summary - -- Implement `ResourceType` (6 values) and `SandboxStrategy` (6 values + helper properties) enums for classifying project resources and sandboxing behavior -- Add `Resource` Pydantic model with ULID validation, frozen immutability, and computed properties (`supports_sandbox`, `can_write`, `get_sandbox_path`) -- Add `ValidationConfig` model for project validation commands with `get_all_commands()` and `has_any_validation()` helpers -- Add `ContextConfig` model for context indexing/filtering with default ignore patterns (`.git/`, `node_modules/`, etc.) that merge with user-provided patterns -- Extend the existing `Project` model with `project_id` (ULID), `namespace` (validated, reserved names rejected), `description`, `tags`, `resources`, `validation_config`, and `context_config` -- all with defaults for full backward compatibility with existing code - -#### Test Coverage - -90 BDD scenarios across 3 feature files, 211 steps, all passing. Full regression suite: 1702/1703 scenarios pass (1 pre-existing failure unrelated to this change). - -#### Changed Files - -| File | Change | -|------|--------| -| `src/cleveragents/domain/models/core/resource.py` | Added `Resource` model, ULID pattern constant | -| `src/cleveragents/domain/models/core/project.py` | Added `ValidationConfig`, `ContextConfig`; extended `Project` with B1.1 fields and helpers | -| `src/cleveragents/domain/models/core/__init__.py` | Exported `Resource`, `ValidationConfig`, `ContextConfig` | -| `features/resource_model.feature` | 47 scenarios for enums + Resource model | -| `features/project_config_model.feature` | 19 scenarios for ValidationConfig + ContextConfig | -| `features/project_model.feature` | 24 scenarios for Project extensions | -| `features/steps/resource_model_steps.py` | Step definitions for resource tests | -| `features/steps/project_config_model_steps.py` | Step definitions for config tests | -| `features/steps/project_model_steps.py` | Step definitions for project tests | - -#### Unblocks - -This completes the foundation layer. The following stages are now unblocked: -- **B2** -- Project CLI Commands -- **B5** -- Project Persistence (Alembic migrations + repositories) -- **B3.3-B3.4** -- Sandbox Implementations (also needs Luis's B3.1-B3.2) - ---- - -## Overview - -Stage B1 establishes the core domain models for projects and resources in CleverAgents. These are the foundational data structures that all downstream stages (CLI, persistence, services, sandboxing) depend on. - -All models follow **ADR-004 (Pydantic Validation)** and were built using the **BDD-First** workflow defined in `IMPLEMENTATION_PLAYBOOK.md`. - ---- - -## What Was Implemented - -### B1.3 - `ResourceType` Enum - -**File:** `src/cleveragents/domain/models/core/resource.py:22-34` - -A `str` enum classifying the six types of external data sources a project can reference: - -| Value | Description | -|-------|-------------| -| `GIT_REPOSITORY` | Git-based source code repository | -| `FILESYSTEM` | Local or mounted filesystem directory | -| `DATABASE` | Database connection | -| `API_ENDPOINT` | Remote API service | -| `DOCUMENT_CORPUS` | Collection of documents | -| `CLOUD_INFRASTRUCTURE` | Cloud provider resources | - -String-based (`str, Enum`) so values serialize naturally to JSON and can be compared as strings. - ---- - -### B1.4 - `SandboxStrategy` Enum - -**File:** `src/cleveragents/domain/models/core/resource.py:37-67` - -A `str` enum defining how resource modifications are isolated during plan execution: - -| Value | `supports_rollback` | `is_copy_based` | -|-------|:-------------------:|:---------------:| -| `GIT_WORKTREE` | True | False | -| `COPY_ON_WRITE` | True | True | -| `OVERLAY` | True | True | -| `TRANSACTION_ROLLBACK` | True | False | -| `VERSIONING` | True | False | -| `NONE` | False | False | - -**Helper properties:** -- `supports_rollback` -- True for all strategies except `NONE` -- `is_copy_based` -- True only for `COPY_ON_WRITE` and `OVERLAY` - ---- - -### B1.2 - `Resource` Pydantic Model - -**File:** `src/cleveragents/domain/models/core/resource.py:70-139` - -An immutable (`frozen=True`) Pydantic model representing a single project resource. - -**Fields:** - -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `resource_id` | `str` | required | ULID (26 chars, pattern-validated) | -| `name` | `str` | required | Alphanumeric + hyphens/underscores, auto-lowercased | -| `type` | `ResourceType` | required | Resource classification enum | -| `location` | `str` | required | Path or URI (min_length=1) | -| `is_remote` | `bool` | `False` | Whether remotely accessible | -| `sandbox_strategy` | `SandboxStrategy` | `NONE` | Isolation strategy | -| `read_only` | `bool` | `False` | Write protection flag | -| `metadata` | `dict[str, Any]` | `{}` | Arbitrary key-value metadata | -| `created_at` | `datetime` | `now()` | Creation timestamp | - -**Validators:** -- `resource_id` -- must match ULID pattern `^[0-9A-HJKMNP-TV-Z]{26}$` -- `name` -- alphanumeric with hyphens/underscores only, auto-lowercased -- `location` -- non-empty string - -**Computed properties:** -- `supports_sandbox` -- True if `sandbox_strategy != NONE` -- `can_write` -- True if `not read_only` -- `get_sandbox_path(base_path)` -- returns `base_path / name` - ---- - -### B1.5 - `ValidationConfig` Model - -**File:** `src/cleveragents/domain/models/core/project.py:31-88` - -Configuration for project validation commands used during plan execution. - -**Fields:** - -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `test_command` | `str \| None` | `None` | Test runner command | -| `lint_command` | `str \| None` | `None` | Linter command | -| `type_check_command` | `str \| None` | `None` | Type checker command | -| `build_command` | `str \| None` | `None` | Build command | -| `custom_commands` | `dict[str, str]` | `{}` | Named custom validation commands | -| `timeout_seconds` | `int` | `300` | Per-command timeout | -| `fail_on_lint_error` | `bool` | `True` | Whether lint errors block validation | - -**Methods:** -- `get_all_commands()` -- returns a `dict[str, str]` of all configured commands (standard + custom), keyed by name (`test`, `lint`, `type_check`, `build`, plus custom keys) -- `has_any_validation()` -- returns `True` if any command is configured - ---- - -### B1.6 - `ContextConfig` Model - -**File:** `src/cleveragents/domain/models/core/project.py:91-138` - -Configuration for how project files are indexed and filtered for AI context windows. - -**Fields:** - -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `ignore_patterns` | `list[str]` | (see below) | File patterns to exclude | -| `include_patterns` | `list[str] \| None` | `None` | File patterns to include (None = all) | -| `max_file_size` | `int` | `1,000,000` | Max file size in bytes (1MB) | -| `max_files` | `int` | `100,000` | Max files to index | -| `indexing_strategy` | `str` | `"full_text"` | Indexing approach | -| `chunking_policy` | `str` | `"smart"` | How files are chunked | -| `chunk_size` | `int` | `1000` | Chunk size in tokens | - -**Default ignore patterns** (always merged in): -``` -.git/, node_modules/, __pycache__/, .venv/, *.pyc, .DS_Store -``` - -User-provided patterns are appended to defaults via `field_validator`, ensuring the defaults are never lost. - ---- - -### B1.1 - `Project` Model Extensions - -**File:** `src/cleveragents/domain/models/core/project.py:180-344` - -The existing `Project` model was extended with new fields while preserving full backward compatibility with legacy code. - -**New fields added:** - -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `project_id` | `str \| None` | `None` | ULID identifier (pattern-validated) | -| `namespace` | `str` | `"local"` | Project namespace for grouping | -| `description` | `str \| None` | `None` | Human-readable description | -| `tags` | `list[str]` | `[]` | Filtering tags | -| `resources` | `list[Resource]` | `[]` | Linked resource references | -| `validation_config` | `ValidationConfig \| None` | `None` | Validation command config | -| `context_config` | `ContextConfig` | `ContextConfig()` | Context indexing config | - -**Legacy fields preserved** (unchanged): -- `id: int | None` -- legacy integer ID -- `path: Path` -- project root path -- `settings: ProjectSettings` -- legacy settings -- `current_plan_id: int | None` -- active plan reference - -**Namespace validation:** -- Pattern: `^(local|[a-z][a-z0-9_]{0,49})$` -- Reserved names rejected: `system`, `internal`, `admin`, `root` - -**Computed property:** -- `is_remote` -- `True` only if the project has resources AND all are remote - -**Helper methods:** -- `namespaced_name` -- property returning `"{namespace}/{name}"` -- `parse_namespaced_name(str)` -- static method splitting `"namespace/name"` into a tuple -- `add_resource(resource)` -- returns a new `Project` with the resource appended -- `remove_resource(name)` -- returns a new `Project` without the named resource -- `get_resource(name)` -- returns `Resource | None` by name lookup - ---- - -## Files Changed - -| File | Action | Lines | -|------|--------|-------| -| `src/cleveragents/domain/models/core/resource.py` | Modified | 140 | -| `src/cleveragents/domain/models/core/project.py` | Modified | 345 | -| `src/cleveragents/domain/models/core/__init__.py` | Modified | 104 | - -## Files Created - -| File | Purpose | Lines | -|------|---------|-------| -| `features/resource_model.feature` | BDD scenarios for Resource + enums | 205 | -| `features/project_config_model.feature` | BDD scenarios for ValidationConfig + ContextConfig | 101 | -| `features/project_model.feature` | BDD scenarios for Project extensions | 125 | -| `features/steps/resource_model_steps.py` | Step definitions for resource tests | ~530 | -| `features/steps/project_config_model_steps.py` | Step definitions for config tests | ~250 | -| `features/steps/project_model_steps.py` | Step definitions for project tests | ~280 | - -## Exports - -All new types are exported via `src/cleveragents/domain/models/core/__init__.py`: - -```python -from cleveragents.domain.models.core import ( - Resource, - ResourceType, - SandboxStrategy, - ValidationConfig, - ContextConfig, -) -``` - ---- - -## Test Coverage - -| Feature File | Scenarios | Steps | Status | -|-------------|:---------:|:-----:|:------:| -| `resource_model.feature` | 47 | 88 | PASSING | -| `project_config_model.feature` | 19 | 65 | PASSING | -| `project_model.feature` | 24 | 58 | PASSING | -| **Total** | **90** | **211** | **ALL PASSING** | - -**Full regression suite:** 1702/1703 scenarios passing. The 1 failure (`plan_lifecycle_cli_coverage.feature:128`) is pre-existing and unrelated to B1. - ---- - -## Design Decisions - -1. **Backward compatibility** -- The existing `Project` fields (`id`, `path`, `settings`, `current_plan_id`) were preserved as-is. All new fields have defaults so existing code that constructs `Project(name=..., path=...)` continues to work. - -2. **`Resource` is frozen** -- Uses `frozen=True` config since resources are value objects. Mutation returns new instances. - -3. **`Project` is not frozen** -- Needs `validate_assignment=True` for the legacy `settings` field and because downstream code assigns to `current_plan_id`. - -4. **`project_id` is optional** -- Set to `str | None` with default `None` so legacy code that uses `id: int` isn't forced to provide a ULID. New code should use `project_id`. - -5. **Namespace defaults to `"local"`** -- Matches the specification's definition that single-machine projects use the `local` namespace. - -6. **Context ignore patterns merge, not replace** -- When users provide custom ignore patterns, defaults (`.git/`, `node_modules/`, etc.) are always preserved via the `field_validator`. - -7. **`add_resource` / `remove_resource` return new instances** -- Uses `model_copy(update=...)` pattern for immutable-style operations on the resource list. diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md deleted file mode 100644 index 1436bad56..000000000 --- a/CURRENT_TASK.md +++ /dev/null @@ -1,114 +0,0 @@ -# Current Task Tracker - -> **Active Workstream:** B1 - Project Data Model (Phase 1: Foundation) -- **COMPLETE** -> **Assignee:** Hamza (Python/RDF Expert, Infrastructure Lead) -> **Workflow:** BDD-First per `IMPLEMENTATION_PLAYBOOK.md` -> **Last Updated:** 2026-02-11 - ---- - -## Active Task - -**Stage B1 is complete.** Next up: B2 (Project CLI Commands) or B5 (Project Persistence). - -| Field | Value | -|-------|-------| -| **Next Task** | B2.1 or B5.1 | -| **Status** | READY TO START | -| **Depends On** | B1 (done) | - ---- - -## Completed Tasks - -| Task ID | Title | File(s) | Completed | -|---------|-------|---------|-----------| -| B1.3 | `ResourceType` enum | `resource.py` | 2026-02-10 | -| B1.3a | Create `resource.py` with proper imports | `resource.py` | 2026-02-10 | -| B1.3b | Define 6 enum values | `resource.py` | 2026-02-10 | -| B1.4 | `SandboxStrategy` enum | `resource.py` | 2026-02-10 | -| B1.4a | Define 6 enum values | `resource.py` | 2026-02-10 | -| B1.4b | Add `supports_rollback`, `is_copy_based` helpers | `resource.py` | 2026-02-10 | -| B1.2 | `Resource` Pydantic model | `resource.py` | 2026-02-11 | -| B1.2a | Create model structure (`frozen=True`) | `resource.py` | 2026-02-11 | -| B1.2b | All fields with defaults | `resource.py` | 2026-02-11 | -| B1.2c | Validators (ULID, name, location) | `resource.py` | 2026-02-11 | -| B1.2d | Properties (`supports_sandbox`, `can_write`, `get_sandbox_path`) | `resource.py` | 2026-02-11 | -| B1.5 | `ValidationConfig` model | `project.py` | 2026-02-11 | -| B1.5a | Create model in `project.py` | `project.py` | 2026-02-11 | -| B1.5b | All fields with defaults | `project.py` | 2026-02-11 | -| B1.5c | Helpers (`get_all_commands`, `has_any_validation`) | `project.py` | 2026-02-11 | -| B1.6 | `ContextConfig` model | `project.py` | 2026-02-11 | -| B1.6a | All fields with defaults | `project.py` | 2026-02-11 | -| B1.6b | Default ignore patterns via field_validator | `project.py` | 2026-02-11 | -| B1.1 | `Project` Pydantic model (extend) | `project.py` | 2026-02-11 | -| B1.1a | Import Resource, extend Project class | `project.py` | 2026-02-11 | -| B1.1b | Identity fields (project_id, namespace, description) | `project.py` | 2026-02-11 | -| B1.1c | Categorization (tags, resources, validation_config, context_config) | `project.py` | 2026-02-11 | -| B1.1d | Timestamp fields | `project.py` | 2026-02-11 | -| B1.1e | `is_remote` computed property | `project.py` | 2026-02-11 | -| B1.1f | Namespace validator (pattern + reserved names) | `project.py` | 2026-02-11 | -| B1.1g | `namespaced_name`, `add_resource`, `remove_resource`, `get_resource` | `project.py` | 2026-02-11 | - -### Tests Written - -| Feature File | Scenarios | Status | -|-------------|-----------|--------| -| `features/resource_model.feature` | 47 scenarios (ResourceType + SandboxStrategy + Resource) | PASSING | -| `features/project_config_model.feature` | 19 scenarios (ValidationConfig + ContextConfig) | PASSING | -| `features/project_model.feature` | 24 scenarios (Project extensions) | PASSING | -| **Total** | **90 scenarios** | **ALL PASSING** | - -### Step Definition Files - -| File | Lines | -|------|-------| -| `features/steps/resource_model_steps.py` | ~530 lines | -| `features/steps/project_config_model_steps.py` | ~250 lines | -| `features/steps/project_model_steps.py` | ~280 lines | - ---- - -## Production Code Changes - -| File | Changes | -|------|---------| -| `src/cleveragents/domain/models/core/resource.py` | Added `Resource` Pydantic model (B1.2) | -| `src/cleveragents/domain/models/core/project.py` | Added `ValidationConfig` (B1.5), `ContextConfig` (B1.6), extended `Project` (B1.1) | -| `src/cleveragents/domain/models/core/__init__.py` | Exported `Resource`, `ValidationConfig`, `ContextConfig` | - ---- - -## Regression Status - -- Full unit test suite: **107/108 features passing** (1702/1703 scenarios) -- The 1 failing scenario (`plan_lifecycle_cli_coverage.feature:128`) is a **pre-existing** failure unrelated to B1 changes -- No regressions introduced - ---- - -## BDD Workflow Checklist (per task) - -Reference: `IMPLEMENTATION_PLAYBOOK.md` Section 2 - -``` -1. [X] Understand task (read implementation_plan.md + specification.md) -2. [X] Write .feature file scenarios -3. [X] Write step definitions in _steps.py -4. [X] Run tests -- must FAIL (no implementation yet) -5. [X] Implement production code -6. [X] Run tests -- must PASS -7. [ ] Write Robot Framework integration test (if applicable) -8. [X] Full validation (nox unit_tests) -9. [X] Update tracking (HAMZA_PROGRESS.md, CURRENT_TASK.md) -``` - ---- - -## Notes - -- Legacy `Project` fields preserved (`id`, `path`, `settings`, `current_plan_id`) for backward compatibility -- `ProjectSettings` and `ProjectStats` untouched -- `Resource` model uses `frozen=True`; `Project` does not (needs `add_resource` etc.) -- `ContextConfig` merges user ignore patterns with defaults via `field_validator` -- Migration chain: `001_initial_schema` -> `4b518923afb2_add_debug_attempts` -> `c3d9b3d0cf3e_add_actors` diff --git a/HAMZA_PROGRESS.md b/HAMZA_PROGRESS.md deleted file mode 100644 index 5a84ec18b..000000000 --- a/HAMZA_PROGRESS.md +++ /dev/null @@ -1,213 +0,0 @@ -# Hamza's Progress Tracker - -> **Role:** Python/RDF Expert, Infrastructure Lead (Workstream B) -> **Last Updated:** 2026-02-11 -> **Overall Progress:** 24 / 238 tasks (10%) - ---- - -## Quick Status Dashboard - -| Stage | Description | Tasks | Done | Status | Blocked By | -|-------|-------------|-------|------|--------|------------| -| **B1** | Project Data Model | 24 | 24 | **COMPLETE** | None | -| **B2** | Project CLI Commands | 32 | 0 | NOT STARTED | B1 | -| **B3.3-B3.4** | Sandbox Implementations | 14 | 0 | NOT STARTED | B1, Luis (B3.1-B3.2) | -| **B4** | Resource Integration | 27 | 0 | NOT STARTED | B3 complete | -| **B5** | Project Persistence | 18 | 0 | NOT STARTED | B1 | -| **SEC5** | Secrets Management | 3 | 0 | NOT STARTED | None | -| **SEC7** | Audit Logging | 3 | 0 | NOT STARTED | DB infra | -| **SESS1** | Session Management | 5 | 0 | NOT STARTED | DB infra | -| **SESS2** | Memory Persistence | 4 | 0 | NOT STARTED | SESS1 | -| **CLI0** | Core System Commands | 3 | 0 | NOT STARTED | Services | -| **CLI1** | Plan Interaction CLI | 4 | 0 | NOT STARTED | Plan services | -| **CLI2** | Configuration Commands | 4 | 0 | NOT STARTED | Config infra | -| **CLI3** | Context Commands | 4 | 0 | NOT STARTED | Context service | -| **CONC3** | Garbage Collection | 4 | 0 | NOT STARTED | Sandbox + checkpoints | -| **CTX1** | Repository Indexing | 3 | 0 | NOT STARTED | Project model | -| **CTX2** | Embedding Index | 3 | 0 | NOT STARTED | CTX1 | -| **D1** | Decision Data Model | 14 | 0 | NOT STARTED | After M3 (Day 14) | -| **D2** | Decision Recording | 14 | 0 | NOT STARTED | D1 | -| **D3** | Decision CLI | 12 | 0 | NOT STARTED | D2 | -| **D4.4-D4.5** | Correction CLI | 5 | 0 | NOT STARTED | Jeff (D4.1-D4.3) | -| **D5** | Decision Persistence | 18 | 0 | NOT STARTED | D1 | -| **E5** | Multi-Project Plans | 8 | 0 | NOT STARTED | E1-E4 (Luis/Jeff) | -| **G4** | Context Tiers | 5 | 0 | NOT STARTED | CTX1/CTX2 | -| **G5** | Cost & Risk Estimation | 4 | 0 | NOT STARTED | Actor framework | -| **C3.6e** | Git Operation Skills | 4 | 0 | NOT STARTED | Jeff (C3.1-C3.5) | -| **F4** | Remote Project Support | 2 | 0 | DEFERRED | F1-F3 (Luis) | - ---- - -## Priority Execution Order - -### Phase 1: Foundation (Days 1-2) -- NO BLOCKERS - -#### Stage B1: Project Data Model -File targets: `src/cleveragents/domain/models/core/resource.py`, `src/cleveragents/domain/models/core/project.py` - -- [X] **B1.3** - Define `ResourceType` enum *(2026-02-10)* - - [X] B1.3a - Create `resource.py` with proper imports - - [X] B1.3b - Define values: GIT_REPOSITORY, FILESYSTEM, DATABASE, API_ENDPOINT, DOCUMENT_CORPUS, CLOUD_INFRASTRUCTURE -- [X] **B1.4** - Define `SandboxStrategy` enum *(2026-02-10)* - - [X] B1.4a - Define values: GIT_WORKTREE, COPY_ON_WRITE, OVERLAY, TRANSACTION_ROLLBACK, VERSIONING, NONE - - [X] B1.4b - Add helpers: `supports_rollback`, `is_copy_based` -- [X] **B1.2** - Define `Resource` Pydantic model *(2026-02-11)* - - [X] B1.2a - Create model structure (`frozen=True`) - - [X] B1.2b - Fields: resource_id, name, type, location, is_remote, sandbox_strategy, read_only, metadata, created_at - - [X] B1.2c - Validators: ULID, name (alphanumeric + lowercase), min_length - - [X] B1.2d - Properties: `supports_sandbox`, `can_write`, `get_sandbox_path` -- [X] **B1.5** - Define `ValidationConfig` model *(2026-02-11)* - - [X] B1.5a - Create model in `project.py` - - [X] B1.5b - Fields: test_command, lint_command, type_check_command, build_command, custom_commands, timeout_seconds, fail_on_lint_error - - [X] B1.5c - Helpers: `get_all_commands`, `has_any_validation` -- [X] **B1.6** - Define `ContextConfig` model *(2026-02-11)* - - [X] B1.6a - Fields: ignore_patterns, include_patterns, max_file_size, max_files, indexing_strategy, chunking_policy, chunk_size - - [X] B1.6b - Add default ignore patterns via field_validator -- [X] **B1.1** - Define `Project` Pydantic model *(2026-02-11)* - - [X] B1.1a - Import Resource, create Project class - - [X] B1.1b - Identity fields: project_id, name, namespace, description - - [X] B1.1c - Categorization: tags, resources, validation_config, context_config - - [X] B1.1d - Timestamp fields - - [X] B1.1e - `is_remote` computed property - - [X] B1.1f - Namespace validator (pattern + reserved names) - - [X] B1.1g - `namespaced_name` property, `add_resource`, `remove_resource`, `get_resource` - -### Phase 2: CLI Layer (Days 3-4) -- Depends on B1 - -#### Stage B2: Project CLI Commands -File targets: `src/cleveragents/cli/commands/project.py`, `src/cleveragents/application/services/project_service.py` - -- [ ] **B2.1** - Create CLI scaffold + ProjectService - - [ ] B2.1a - Create `project.py` with imports and Typer group - - [ ] B2.1b - Create `ProjectService` in `project_service.py` -- [ ] **B2.2** - `project create` command - - [ ] B2.2a-e - Command signature, namespace parsing, creation, output, service method -- [ ] **B2.3** - `project add-resource` command - - [ ] B2.3a-f - Signature, type validation, location validation, metadata parsing, resource creation, service method -- [ ] **B2.4** - `project remove-resource` command - - [ ] B2.4a-c - Signature, removal logic, service method -- [ ] **B2.5** - `project list` command - - [ ] B2.5a-e - Signature, query/filtering, table output, JSON output, service method -- [ ] **B2.6** - `project show` command - - [ ] B2.6a-d - Signature, fetch + rich display, JSON output, service method -- [ ] **B2.7** - `project set-validation` command - - [ ] B2.7a-c - Signature, config update, service method -- [ ] **B2.8** - `project delete` command - - [ ] B2.8a-d - Signature, deletion checks, deletion, service method -- [ ] **B2.9** - Register commands in `main.py` - - [ ] B2.9a - Import and register - - [ ] B2.9b - DI wiring for ProjectService - -### Phase 3: Sandbox Implementations (Days 3-5) -- Depends on B1 + Luis (B3.1-B3.2) - -#### Stage B3.3-B3.4: Sandbox Implementations -File targets: `src/cleveragents/infrastructure/sandbox/git_worktree.py`, `src/cleveragents/infrastructure/sandbox/filesystem.py` - -- [ ] **B3.3** - `GitWorktreeSandbox` - - [ ] B3.3a-h - Constructor, create(), _run_git(), get_path(), commit(), rollback(), cleanup(), error handling -- [ ] **B3.4** - `FilesystemSandbox` - - [ ] B3.4a-f - Constructor, create() with copytree, get_path(), commit() with diff, rollback(), cleanup() - -### Phase 4: Resource Service + Persistence (Days 6-8) - -#### Stage B4: Resource Integration (depends on B3) -File targets: `src/cleveragents/application/services/resource_service.py`, `src/cleveragents/domain/models/core/resource_access.py` - -- [ ] **B4.1** - Resource access types (AccessMode, ResourceAccess, ResourceAccessTracker) -- [ ] **B4.2** - ResourceService scaffold + config -- [ ] **B4.3** - `access_resource()` method (read/write/upgrade) -- [ ] **B4.4** - Lazy sandboxing pattern -- [ ] **B4.5** - Commit and rollback methods -- [ ] **B4.6** - Cleanup hooks (plan completion, failure, exit, startup) -- [ ] **B4.7** - PlanLifecycleService integration + DI wiring - -#### Stage B5: Project Persistence (depends on B1, parallel with B4) -File targets: `models.py`, `repositories.py`, Alembic migrations - -- [ ] **B5.1** - Alembic migration: `projects` table -- [ ] **B5.2** - Alembic migration: `resources` table (FK to projects) -- [ ] **B5.3** - `ProjectModel` SQLAlchemy model + domain conversion -- [ ] **B5.4** - `ResourceModel` SQLAlchemy model + domain conversion -- [ ] **B5.5** - `ProjectRepository` (CRUD: create, get_by_id, get_by_name, get_with_resources, list_all, update, delete) -- [ ] **B5.6** - `ResourceRepository` (CRUD: create, get_by_project, get_by_name, delete) - -### Phase 5: Security & Sessions (Days 6-10) - -- [ ] **SEC5.1-5.3** - Secrets masking, env var handling, prevent secrets in code -- [ ] **SEC7.1-7.3** - Apply audit logging, audit_log migration, audit list CLI -- [ ] **SESS1.1-1.5** - Session model, service, migration, persistence, CLI -- [ ] **SESS2.1-2.4** - Memory service updates, conversation_history migration, config, warning - -### Phase 6: CLI Commands (Days 10-14) - -- [ ] **CLI0.1-0.3** - version, info, diagnostics commands -- [ ] **CLI1.1-1.4** - plan prompt, plan diff, plan diff --correction, plan artifacts -- [ ] **CLI2.1-2.4** - config set/get/list, providers list -- [ ] **CLI3.1-3.4** - project context set/show, actor context set/show -- [ ] **CONC3.1-3.4** - Sandbox GC, checkpoint cleanup, session cleanup, auto-cleanup -- [ ] **CTX1.1-1.3** - IndexingService, file tree, language detection -- [ ] **CTX2.1-2.3** - VectorStore integration, semantic search, optional embeddings - -### Phase 7: Decision Tree (Days 15-21) -- After M3 merge - -- [ ] **D1.1-1.4** - DecisionType enum, ContextSnapshot, Decision model, helpers -- [ ] **D2.1-2.6** - DecisionService, record_decision, tree queries, snapshots, strategy integration -- [ ] **D3.1-3.4** - plan tree CLI, plan explain CLI, JSON output, --guidance-file -- [ ] **D4.4-4.5** - plan correct revert CLI, plan correct append CLI (needs Jeff D4.1-D4.3) -- [ ] **D5.1-5.6** - Alembic migrations (decisions, dependencies, corrections, snapshots) + models + repos - -### Phase 8: Advanced Features (Days 22-35) - -- [ ] **E5.1-5.4** - Multi-project plan support (needs E1-E4) -- [ ] **G4.1-4.5** - Context tiers: hot/warm/cold + actor views + promotion/demotion -- [ ] **G5.1-5.4** - Cost estimation actor, token/cost calc, risk assessment, display -- [ ] **C3.6e** - Git operation skills (needs Jeff C3.1-C3.5) -- [ ] **F4.1-4.2** - Remote project support (DEFERRED, needs Luis F1-F3) - ---- - -## Merge Point Checkpoints - -| Merge | Day | Hamza's Verification Task | -|-------|-----|---------------------------| -| **M1** | 8 | M1.7 - Verify Plan-Actor binding with real actors | -| **M3** | 14 | M3.4 - Verify sandbox commit applies changes to original | -| **M4** | 21 | M4.1 - Decision recording captures context; M4.3 - Tree visualization works | -| **M6** | 30 | M6.4 - Deep subplan hierarchies; M6.7 - Cold tier queries | - ---- - -## Deliverable File Index - -| Category | File Path | -|----------|-----------| -| Domain Models | `src/cleveragents/domain/models/core/resource.py` | -| | `src/cleveragents/domain/models/core/project.py` | -| | `src/cleveragents/domain/models/core/resource_access.py` | -| | `src/cleveragents/domain/models/core/decision.py` | -| | `src/cleveragents/domain/models/core/session.py` | -| CLI Commands | `src/cleveragents/cli/commands/project.py` | -| | `src/cleveragents/cli/commands/plan.py` (extend) | -| | `src/cleveragents/cli/main.py` (register) | -| Services | `src/cleveragents/application/services/project_service.py` | -| | `src/cleveragents/application/services/resource_service.py` | -| | `src/cleveragents/application/services/decision_service.py` | -| | `src/cleveragents/application/services/session_service.py` | -| | `src/cleveragents/application/services/indexing_service.py` | -| Sandbox | `src/cleveragents/infrastructure/sandbox/git_worktree.py` | -| | `src/cleveragents/infrastructure/sandbox/filesystem.py` | -| Database | `src/cleveragents/infrastructure/database/models.py` (extend) | -| | `src/cleveragents/infrastructure/database/repositories.py` (extend) | -| Config | `src/cleveragents/config/settings.py` (extend) | -| Migrations | projects, resources, decisions, decision_dependencies, correction_attempts, context_snapshots, audit_log, sessions, conversation_history | -| Tests (Behave) | `features/resource_model.feature`, `features/project_model.feature`, `features/project_cli.feature`, etc. | -| Tests (Robot) | `robot/project_integration.robot`, `robot/sandbox_integration.robot`, etc. | - ---- - -## Notes / Blockers Log - -| Date | Note | -|------|------| -| 2026-02-10 | Initial tracker created. All 238 tasks pending. Starting with B1. | diff --git a/IMPLEMENTATION_PLAYBOOK.md b/IMPLEMENTATION_PLAYBOOK.md deleted file mode 100644 index 35b584a85..000000000 --- a/IMPLEMENTATION_PLAYBOOK.md +++ /dev/null @@ -1,718 +0,0 @@ -# CleverAgents Implementation Playbook - -> Generic guide for implementing any feature, fix, or task in the CleverAgents codebase. -> Use this as the base workflow every time. No exceptions. - ---- - -## Table of Contents - -1. [Development Approach: BDD-First (Not Pure TDD)](#1-development-approach-bdd-first) -2. [The Standard Workflow](#2-the-standard-workflow) -3. [Architecture Rules](#3-architecture-rules) -4. [File Organization](#4-file-organization) -5. [Coding Standards](#5-coding-standards) -6. [Testing Guide](#6-testing-guide) -7. [Database Changes Guide](#7-database-changes-guide) -8. [CLI Commands Guide](#8-cli-commands-guide) -9. [Commands Reference](#9-commands-reference) -10. [Checklist Templates](#10-checklist-templates) - ---- - -## 1. Development Approach: BDD-First - -This project uses **BDD-First** (Behavior-Driven Development): - -### The Core Rule - -> **Write the `.feature` file FIRST. Then the step definitions. Then the implementation. Never the other way around.** - ---- - -## 2. The Standard Workflow - -Every task follows this exact 8-step sequence. No skipping steps. - -### Step 1: Understand the Task - -- Read the task description in `implementation_plan.md` -- Read the specification in `docs/specification.md` if the domain is unclear -- Identify the deliverable files (models, services, CLI, tests) -- Identify dependencies (what must exist before you start) - -### Step 2: Write the Behave Feature File - -Create `features/.feature`: - -```gherkin -Feature: Resource Type Management - As a developer - I want to define resource types for projects - So that the system can handle different resource kinds appropriately - - Scenario: Create a valid git repository resource type - Given I have the ResourceType enum imported - When I access ResourceType.GIT_REPOSITORY - Then the value should be "git_repository" - - Scenario: Reject invalid resource type - When I try to create a resource with type "invalid_type" - Then a validation error should be raised - And the error should mention "resource type" - - Scenario: SandboxStrategy supports rollback check - Given I have a GIT_WORKTREE sandbox strategy - When I check if it supports rollback - Then the result should be true - - Scenario: SandboxStrategy copy-based check - Given I have a COPY_ON_WRITE sandbox strategy - When I check if it is copy based - Then the result should be true -``` - -**Rules:** -- One `.feature` per logical domain concept -- Use `Background:` for shared setup -- Cover happy path, validation errors, edge cases -- Name: `features/.feature` - -### Step 3: Write Step Definitions - -Create `features/steps/_steps.py`: - -```python -"""Step definitions for Resource Type tests.""" - -from behave import given, then, when -from behave.runner import Context - - -@given("I have the ResourceType enum imported") -def step_import_resource_type(context: Context) -> None: - """Import ResourceType enum.""" - from cleveragents.domain.models.core.resource import ResourceType - context.resource_type_cls = ResourceType - - -@when("I access ResourceType.GIT_REPOSITORY") -def step_access_git_repo(context: Context) -> None: - """Access the GIT_REPOSITORY enum value.""" - context.result = context.resource_type_cls.GIT_REPOSITORY - - -@then('the value should be "{expected}"') -def step_check_value(context: Context, expected: str) -> None: - """Verify enum value.""" - assert context.result.value == expected, ( - f"Expected '{expected}', got '{context.result.value}'" - ) -``` - -**Rules:** -- Step file name matches feature file: `foo.feature` -> `foo_steps.py` -- Steps private to one feature MUST live in that feature's step file -- Use `context` to pass state between steps -- Always type-annotate: `context: Context`, return `-> None` -- Always add a docstring to every step function -- Use `context.error` pattern for testing validation errors -- NEVER add placeholder steps -- implement fully or don't add - -### Step 4: Run Tests (They Should FAIL) - -```bash -nox -e unit_tests -- features/.feature -``` - -This MUST fail because the implementation doesn't exist yet. If it passes, your tests are wrong. - -### Step 5: Implement the Production Code - -Follow the architecture layers (see Section 3). Implement in this order: - -1. **Domain models** (`src/cleveragents/domain/models/core/`) -2. **Domain interfaces** (protocols, repository interfaces) -3. **Infrastructure** (DB models, repositories, migrations) -4. **Application services** (`src/cleveragents/application/services/`) -5. **DI wiring** (`src/cleveragents/application/container.py`) -6. **CLI commands** (`src/cleveragents/cli/commands/`) - -### Step 6: Run Tests (They Should PASS) - -```bash -# Run your specific feature -nox -e unit_tests -- features/.feature - -# Run type checking -nox -e typecheck - -# Run linting -nox -e lint -``` - -Fix any failures. Iterate between Step 5 and Step 6 until green. - -### Step 7: Write Robot Framework Integration Test - -Create `robot/.robot`: - -```robot -*** Settings *** -Documentation Integration tests for Resource types -Library OperatingSystem -Library Process -Resource common.resource - -*** Test Cases *** -Create Project With Git Resource - [Documentation] Verify project creation with git resource end-to-end - Setup Test Environment - ${result}= Run Python Script - ... from cleveragents.domain.models.core.resource import Resource, ResourceType - ... r = Resource(resource_id="01HXYZ...", name="repo", type=ResourceType.GIT_REPOSITORY, location="/tmp/repo") - ... print(r.name) - Should Be Equal ${result} repo - Cleanup Test Environment -``` - -Run: `nox -e integration_tests -- robot/.robot` - -### Step 8: Full Validation - -```bash -# Run everything -nox - -# This executes: lint, format check, typecheck, unit tests, integration tests, docs build, coverage check -``` - -ALL must pass. Coverage must stay above 85%. - -### Step 9: Update Tracking - -- Check off the task in `implementation_plan.md` (`[ ]` -> `[X]`) -- Update `HAMZA_PROGRESS.md` with completion status -- Commit with conventional message: `feat(project): add ResourceType enum (B1.3)` - ---- - -## 3. Architecture Rules - -### Layer Diagram - -``` -CLI (Typer) - | - v -Application Services (business logic orchestration) - | - v -Domain Models (Pydantic) + Domain Interfaces (Protocols) - | - v -Infrastructure (SQLAlchemy, Alembic, Sandbox, Providers) - | - v -DI Container (dependency-injector) wires everything together -``` - -### Dependency Rules - -- **CLI** depends on **Application Services** only (never infrastructure directly) -- **Application Services** depend on **Domain Models** + **Domain Interfaces** -- **Infrastructure** implements **Domain Interfaces** -- **Domain Models** depend on NOTHING (pure data + validation) -- **DI Container** wires interfaces to implementations - -### Design Patterns in Use - -| Pattern | Where | Example | -|---------|-------|---------| -| Repository | Data access | `ProjectRepository`, `PlanRepository` | -| Unit of Work | Transactions | `UnitOfWork` + `UnitOfWorkContext` | -| Dependency Injection | Wiring | `Container` with providers.Factory/Singleton | -| Strategy | Sandboxing | `GitWorktreeSandbox`, `FilesystemSandbox` | -| State Machine | Plan lifecycle | `PlanPhase` transitions with `can_transition()` | -| Factory | Object creation | `providers.Factory(ProjectService, ...)` | -| Protocol | Interfaces | `AIProviderInterface`, `SandboxProtocol` | - ---- - -## 4. File Organization - -### Where Things Go - -| What | Where | Naming | -|------|-------|--------| -| Domain model | `src/cleveragents/domain/models/core/.py` | snake_case, singular noun | -| Domain enum | Same file as the model it belongs to | PascalCase | -| Repository interface | `src/cleveragents/domain/repositories/` | `_repository.py` | -| SQLAlchemy model | `src/cleveragents/infrastructure/database/models.py` | `Model` | -| Repository impl | `src/cleveragents/infrastructure/database/repositories.py` | `Repository` | -| Alembic migration | `alembic/versions/` | Auto-generated name | -| Application service | `src/cleveragents/application/services/_service.py` | `Service` | -| CLI command group | `src/cleveragents/cli/commands/.py` | Typer app | -| Behave feature | `features/.feature` | snake_case | -| Behave steps | `features/steps/_steps.py` | matches feature | -| Mocks | `features/mocks/` | `mock_.py` | -| Robot test | `robot/.robot` | snake_case | -| Config/Settings | `src/cleveragents/config/settings.py` | Extend existing | - -### File Size Limit - -**500 lines max per file.** If approaching this, split into focused submodules. - -### Exports - -Every `__init__.py` in the models package MUST export all public types via `__all__`. - ---- - -## 5. Coding Standards - -### Type Annotations - -```python -# REQUIRED on all functions, methods, and variables where not obvious -def create_project(self, name: str, namespace: str | None = None) -> Project: - ... - -# Use | for unions (Python 3.13) -value: str | None = None -items: list[str] | tuple[str, ...] = [] - -# Use TYPE_CHECKING guard for import-only types -from __future__ import annotations -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from cleveragents.domain.models.core.project import Project -``` - -### Argument Validation (MANDATORY for all public/protected methods) - -```python -def process(self, data: list[str], threshold: int) -> Result: - """Process data with threshold. - - Args: - data: Non-empty list of strings to process. - threshold: Value between 0 and 100. - - Returns: - Processing result. - - Raises: - ValueError: If data is empty or threshold out of range. - TypeError: If data contains non-string items. - """ - if not data: - raise ValueError("data cannot be empty") - if not all(isinstance(item, str) for item in data): - raise TypeError("data must contain only strings") - if threshold < 0 or threshold > 100: - raise ValueError(f"threshold must be between 0 and 100, got {threshold}") - # ... actual logic -``` - -### Error Handling - -```python -# GOOD: Catch specific, add context, re-raise or handle -try: - result = self.repository.get_by_id(project_id) -except DatabaseError as e: - raise ProjectNotFoundError(f"Failed to fetch project {project_id}") from e - -# BAD: Never do these -except: # bare except -except Exception: # too broad without re-raise -except Exception as e: # swallowing the error - return None # silent failure -``` - -### Pydantic Model Pattern - -```python -"""Resource domain model. - -Implements ADR-004 (Data Validation) and specification section X.Y. -""" - -from datetime import datetime -from enum import Enum -from pydantic import BaseModel, ConfigDict, Field, field_validator - -ULID_PATTERN = r"^[0-9A-HJKMNP-TV-Z]{26}$" - - -class ResourceType(str, Enum): - """Types of resources a project can reference.""" - GIT_REPOSITORY = "git_repository" - FILESYSTEM = "filesystem" - - -class Resource(BaseModel): - """A project resource reference. - - Resources represent external data sources or repositories - that a project operates on. - """ - resource_id: str = Field(..., description="Unique ULID", pattern=ULID_PATTERN) - name: str = Field(..., min_length=1, max_length=255, description="Human-readable name") - type: ResourceType = Field(..., description="Resource classification") - location: str = Field(..., min_length=1, description="Path or URI") - created_at: datetime = Field(default_factory=datetime.now) - - @field_validator("name") - @classmethod - def validate_name(cls: type["Resource"], v: str) -> str: - """Enforce naming rules.""" - if not v.replace("-", "").replace("_", "").isalnum(): - raise ValueError("Name must be alphanumeric with hyphens/underscores") - return v.lower() - - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - use_enum_values=False, - frozen=True, - ) -``` - -### Import Rules - -- ALL imports at top of file (no inline imports) -- Exception: `if TYPE_CHECKING:` guard -- Use absolute imports: `from cleveragents.domain.models.core.resource import Resource` -- Order: stdlib -> third-party -> local (enforced by ruff `I` rule) - ---- - -## 6. Testing Guide - -### Behave (Unit/Behavioral Tests) - -| Aspect | Rule | -|--------|------| -| Location | `features/*.feature` + `features/steps/*_steps.py` | -| Runner | `nox -e unit_tests` (NEVER run behave directly) | -| Mock placement | `features/mocks/` ONLY | -| Coverage | Must stay above 85% (`nox -e coverage_report`) | -| Parallelism | Tests run in parallel via behave-parallel | -| Tags | `@discovery` for Phase 0 tests (excluded by default) | -| Environment | `features/environment.py` handles setup/teardown | - -#### Error Testing Pattern - -```gherkin -Scenario: Reject resource with empty name - When I try to create a resource with name "" - Then a validation error should be raised - And the error should mention "name" -``` - -```python -@when('I try to create a resource with name "{name}"') -def step_try_create_resource(context: Context, name: str) -> None: - """Attempt resource creation that may fail.""" - context.error = None - try: - context.resource = Resource( - resource_id="01HXYZABC123DEF456GHJ789KL", - name=name, - type=ResourceType.GIT_REPOSITORY, - location="/tmp/repo", - ) - except (ValidationError, ValueError) as e: - context.error = e - - -@then("a validation error should be raised") -def step_check_error_raised(context: Context) -> None: - """Verify an error was captured.""" - assert context.error is not None, "Expected a validation error but none was raised" - - -@then('the error should mention "{substring}"') -def step_check_error_message(context: Context, substring: str) -> None: - """Verify error message contains expected text.""" - assert substring.lower() in str(context.error).lower(), ( - f"Expected error to mention '{substring}', got: {context.error}" - ) -``` - -### Robot Framework (Integration Tests) - -| Aspect | Rule | -|--------|------| -| Location | `robot/*.robot` | -| Runner | `nox -e integration_tests` (NEVER run robot directly) | -| Shared resources | `robot/common.resource` | -| Python helpers | `robot/*.py` in same directory | -| Parallelism | Via pabot | -| Tags | `@slow` excluded from default runs | - -#### Integration Test Pattern - -```robot -*** Settings *** -Documentation Integration tests for Project persistence -Library OperatingSystem -Library Process -Resource common.resource - -*** Test Cases *** -Create And Retrieve Project From Database - [Documentation] End-to-end project CRUD via database - Setup Test Environment - ${project_id}= Create Test Project my-project default - ${retrieved}= Get Project By Id ${project_id} - Should Be Equal ${retrieved.name} my-project - Cleanup Test Environment -``` - -### What to Test - -| Layer | Test Type | What to Cover | -|-------|-----------|---------------| -| Domain Model | Behave | Creation, validation, computed properties, state transitions, edge cases | -| Service | Behave | Business logic, orchestration, error handling | -| Repository | Robot | CRUD operations, queries, transactions, UoW | -| CLI | Robot | Command execution, output format, error messages | -| Sandbox | Robot | File operations, git operations, cleanup | - ---- - -## 7. Database Changes Guide - -### Adding a New Table - -1. **Define the SQLAlchemy model** in `src/cleveragents/infrastructure/database/models.py`: - -```python -class ProjectModel(Base): - __tablename__ = "projects" - id = Column(Integer, primary_key=True, autoincrement=True) - project_id = Column(String(26), nullable=False, unique=True) # ULID - name = Column(String(255), nullable=False, unique=True) - namespace = Column(String(100), nullable=False, default="default") - settings_json = Column(JSON, nullable=False, default=dict) - created_at = Column(DateTime, nullable=False, default=datetime.now) -``` - -2. **Create Alembic migration**: - -```python -"""Add projects and resources tables. - -Revision ID: -Revises: c3d9b3d0cf3e # MUST chain from latest migration -Create Date: 2026-02-10 -""" -from collections.abc import Sequence -import sqlalchemy as sa -from alembic import op - -revision: str = "" -down_revision: str | Sequence[str] | None = "c3d9b3d0cf3e" - -def upgrade() -> None: - op.create_table("projects", - sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), - sa.Column("project_id", sa.String(26), nullable=False), - sa.Column("name", sa.String(255), nullable=False), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint("project_id"), - sa.UniqueConstraint("name"), - ) - -def downgrade() -> None: - op.drop_table("projects") -``` - -3. **Implement repository** in `src/cleveragents/infrastructure/database/repositories.py` - -4. **Register in UoW** as lazy property on `UnitOfWorkContext` - -5. **Wire in DI container** if needed - -Current migration chain: `001_initial_schema` -> `4b518923afb2_add_debug_attempts` -> `c3d9b3d0cf3e_add_actors` - ---- - -## 8. CLI Commands Guide - -### Adding a New Command Group - -1. **Create command file** `src/cleveragents/cli/commands/.py`: - -```python -"""Project management CLI commands.""" - -from typing import Annotated, Optional - -import typer -from rich.console import Console -from rich.table import Table - -from cleveragents.application.container import get_container - -app = typer.Typer(help="Manage projects") -console = Console() - - -@app.command() -def create( - name: Annotated[str, typer.Argument(help="Project name")], - namespace: Annotated[Optional[str], typer.Option("--namespace", "-n", help="Project namespace")] = None, - description: Annotated[Optional[str], typer.Option("--description", "-d", help="Description")] = None, -) -> None: - """Create a new project.""" - container = get_container() - service = container.project_service() - - project = service.create_project(name=name, namespace=namespace or "default", description=description) - - console.print(f"[green]Created project:[/green] {project.namespaced_name}") -``` - -2. **Register in main.py** (`src/cleveragents/cli/main.py`): - -```python -from cleveragents.cli.commands.project import app as project_app -app.add_typer(project_app, name="project") -``` - ---- - -## 9. Commands Reference - -### Development Commands (Always Use nox) - -```bash -# Full validation suite (lint + format + typecheck + tests + docs + build + coverage) -nox - -# Individual sessions -nox -e lint # Ruff linter -nox -e format # Ruff formatter -nox -e typecheck # Pyright strict mode -nox -e unit_tests # Behave BDD tests (parallel) -nox -e integration_tests # Robot Framework tests (parallel) -nox -e coverage_report # Behave with coverage (>85% required) -nox -e docs # Build MkDocs -nox -e build # Build wheel - -# Run a single feature file -nox -e unit_tests -- features/resource_model.feature - -# Run a single robot file -nox -e integration_tests -- robot/project_integration.robot -``` - -### NEVER Run Directly - -```bash -# WRONG - never do this -behave features/foo.feature -robot robot/foo.robot -pytest -python -m pytest -ruff check . -pyright -``` - -### Git Workflow - -```bash -# Commit convention -git commit -m "feat(project): add ResourceType enum (B1.3)" -git commit -m "test(project): add resource model BDD scenarios" -git commit -m "fix(sandbox): handle missing worktree directory" - -# Types: feat, fix, chore, docs, test, refactor, perf, style -# Scope: the domain area (project, plan, sandbox, decision, cli, etc.) -``` - ---- - -## 10. Checklist Templates - -### New Domain Model Checklist - -``` -- [ ] Write `.feature` file with all scenarios -- [ ] Write step definitions in `_steps.py` -- [ ] Run `nox -e unit_tests -- features/.feature` (should FAIL) -- [ ] Implement model in `src/cleveragents/domain/models/core/.py` -- [ ] Add exports to `__init__.py` -- [ ] Run `nox -e unit_tests -- features/.feature` (should PASS) -- [ ] Run `nox -e typecheck` (should PASS) -- [ ] Run `nox -e lint` (should PASS) -- [ ] Update `implementation_plan.md` checkboxes -- [ ] Update `HAMZA_PROGRESS.md` -- [ ] Commit: `feat(): ()` -``` - -### New Database Table Checklist - -``` -- [ ] Write Robot integration test in `robot/.robot` -- [ ] Add SQLAlchemy model to `models.py` -- [ ] Create Alembic migration (chain from latest revision) -- [ ] Implement repository in `repositories.py` -- [ ] Add lazy property to `UnitOfWorkContext` -- [ ] Wire in DI container if needed -- [ ] Run `nox -e integration_tests -- robot/.robot` (should PASS) -- [ ] Run full `nox` suite -- [ ] Update tracking docs -- [ ] Commit: `feat(db): add
persistence ()` -``` - -### New CLI Command Checklist - -``` -- [ ] Write Behave feature for command behavior -- [ ] Write Robot test for end-to-end command execution -- [ ] Create command file in `cli/commands/` -- [ ] Create or extend application service -- [ ] Wire service in DI container -- [ ] Register command in `cli/main.py` -- [ ] Run `nox` full suite -- [ ] Update tracking docs -- [ ] Commit: `feat(cli): add command ()` -``` - -### Bug Fix Checklist - -``` -- [ ] Write a Behave scenario that reproduces the bug (should FAIL) -- [ ] Identify root cause -- [ ] Implement fix -- [ ] Run failing scenario (should PASS) -- [ ] Run full `nox` suite (no regressions) -- [ ] Commit: `fix(): ()` -``` - ---- - -## Quick Decision Guide - -| Question | Answer | -|----------|--------| -| Where do I put a new model? | `src/cleveragents/domain/models/core/` | -| Where do I put tests? | `features/` (Behave) and `robot/` (Robot Framework) | -| Where do mocks go? | `features/mocks/` ONLY | -| How do I run tests? | `nox -e unit_tests` or `nox -e integration_tests` | -| What Python version? | 3.13 only | -| How do I check types? | `nox -e typecheck` | -| How do I format code? | `nox -e format` | -| What's the coverage target? | 85% minimum | -| Can I use pytest? | NO. Behave for unit, Robot for integration. | -| Can I skip type annotations? | NO. Everything must be typed. | -| Can I add `# type: ignore`? | NO. Fix the type issue instead. | -| Can I put test code in `src/`? | NO. Never. | -| File size limit? | 500 lines max | -| Commit message format? | `(): ` | diff --git a/notes.md b/notes.md deleted file mode 100644 index d33f1a0b1..000000000 --- a/notes.md +++ /dev/null @@ -1,12 +0,0 @@ -# Some Notes For me - -This is a markdown file where I can jot down notes for myself. - - -## Error - -this error was reported: -Model: Opus 4.6 -``` -Error [193:63] Arguments missing for parameters "auto_build", "auto_apply", "confirm_apply", "max_context_size", "default_model" -``` -- 2.52.0 From ccacbcc64fb1917c3199f131e247aea18b6eed19 Mon Sep 17 00:00:00 2001 From: khyari hamza Date: Thu, 12 Feb 2026 15:54:38 +0100 Subject: [PATCH 08/11] chore: reset implementation_plan.md to match master --- implementation_plan.md | 168 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 3 deletions(-) diff --git a/implementation_plan.md b/implementation_plan.md index b65212726..fcc6918e9 100644 --- a/implementation_plan.md +++ b/implementation_plan.md @@ -8,7 +8,7 @@ - **Single documentation surface**: Do not create auxiliary notes elsewhere unless explicitly required. All architectural updates, troubleshooting outcomes, and contextual knowledge must flow back into this markdown file. - **Sequential discipline**: Always begin with the first unchecked item in the checklist. Do not progress until that item, its documentation update, and its testing sub-items (including any spawned remediation tasks) are fully resolved. - **USE MODERN PYTHON TOOLING**: This is a cutting-edge Python project that must use modern build tools and workflows. NO Makefiles, NO legacy approaches, NO helper scripts. Use Hatch exclusively for project management, nox for task automation, pyproject.toml for all configuration. Commands should be Python-native (e.g., `hatch env create`, `nox -s test`) not shell scripts or make targets. All tooling must be from the current Python ecosystem (2024+). When current tooling (such as "Behave" and "Robot Framework") can be used to solve a problem, use them rather than adding new tooling, keep it simple. NO wrapper scripts - use tools directly as designed. -- **Unit + integration testing mandate**: For every coding task, author or update both unit and integration tests, run them, and achieve passing results before marking the task complete. Testing subtasks are non-optional. +- **Unit + integration + asv testing mandate**: For every coding task, author or update must include asv (airspeed velocity) performance, unit and integration tests, run them, and achieve passing results before marking the task complete. Testing subtasks are non-optional. - **Behavior-driven testing stack**: Use Behave feature suites under `features/` for unit-level and scenario tests and Robot Framework suites under `robot/` for integration and end-to-end coverage. Keep both synchronized with the code under test and document all updates in this plan. - **Do not use pytest style unit tests**: Under no circumstances should you write pytest styled unit tests, all unit tests should be Behave based (as noted in the last bullet point), which follows the Cucumber/Gherkin style of tests as seen under `features/`, this is why there is intentionally no `tests/` folder. - **Test execution via nox**: Run every unit, integration, Behave, Robot, and benchmark suite exclusively through the designated `nox` sessions (e.g., `nox -s unit_tests`, `nox -s integration_tests`). Do not invoke `behave`, `robot`, or similar runners directly; if a `nox` session is missing required tooling, add the dependency to the session before rerunning. @@ -309,6 +309,168 @@ The following work from the previous implementation has been completed and will - Added 15 Behave test scenarios in `features/action_cli.feature` - Total test scenarios: 96 (81 + 15) + +**2026-02-09**: Task Q0.6b Complete - README.md Setup Instructions [Brent] + +- Updated README.md Quick Start: added `dev` extras to `pip install`, added `scripts/setup-dev.sh` step +- Updated README.md Developing section: fixed `oxt` typo -> `nox`, added quality/security nox sessions (security_scan, dead_code, complexity, pre_commit, adr_compliance), added note about pre-commit hooks, linked to `docs/development/quality-automation.md` + +**2026-02-09**: Ruff Cleanup in src/cleveragents/ - StrEnum Migration [Brent] + +- Fixed all 26 ruff findings in `src/cleveragents/` (all UP042: `str, Enum` -> `StrEnum`) + 12 consequent F401 unused `Enum` imports +- Migrated 26 enum classes across 15 files from `class Foo(str, Enum)` to `class Foo(StrEnum)` +- `StrEnum` (Python 3.11+) is the modern replacement; project targets Python 3.13 +- Semantic difference: `str(StrEnum.MEMBER)` returns the value (e.g., `"foo"`) rather than `"ClassName.MEMBER"` — this is the correct/intended behavior for config/JSON string enums +- Verified: no code uses `str()` on enum members in the old format; all tests pass (304 scenarios, 0 failures) +- Files: 15 domain model files + `memory_service.py` + `providers/registry.py` + +**2026-02-09**: Task Q0.9 Complete - Ruff Lint Findings in features/ [Brent] + +- Fixed all 200 ruff lint findings in `features/` directory -> **0 findings** +- **Config-level suppressions** (168 findings): + - Added `per-file-ignores` in `pyproject.toml` for Behave-specific patterns: + - `features/steps/*.py`: F811 (65 redefined `step_impl` — Behave idiom), E501 (long step decorator strings) + - `features/mocks/*.py`, `features/environment.py`: E501 +- **Manual fixes** (31 findings across 18 files): + - 11x SIM115: `NamedTemporaryFile` refactored to use `with` context manager (`actor_cli_steps.py`, `actor_cli_run_steps.py`) + - 4x UP028: `for/yield` -> `yield from` (google, openai, openrouter, langchain provider steps) + - 3x SIM117: Nested `with` -> single `with` with parenthesized contexts (`plan_full_coverage_steps.py`, `plan_service_steps.py`) + - 3x RUF005: `list + [item]` -> `[*list, item]` unpacking + - 2x B904: Added `from exc` to `raise` inside `except` (enums, retry patterns) + - 2x RUF012: Added `ClassVar` annotations (`vector_store_service_steps.py`) + - 2x SIM105: `try/except/pass` -> `contextlib.suppress(Exception)` + - 1x each: B007 (unused loop var), B018 (noqa suppression), F821 (missing `Any` import), SIM102 (collapsible if), I001 (auto-fixed unsorted import) +- **Verification**: All affected behave tests pass (155 scenarios, 0 failures) +- **Files modified**: `pyproject.toml` (config), `environment.py`, and 17 step files in `features/steps/` + +**2026-02-09**: Task Q0.8 Complete - Bandit Security Findings Remediation [Brent] + +- Fixed all 16 pre-existing bandit findings (2 HIGH, 3 MEDIUM, 11 LOW) -> **0 findings** +- **Security hardening** (HIGH+MEDIUM): + - Replaced `jinja2.Environment` with `jinja2.sandbox.SandboxedEnvironment` in `yaml_template_engine.py` and `stream_router.py` — prevents template injection + - Added `_validate_code_ast()` helper: AST-based pre-validation for `exec()` in `SimpleToolAgent` — rejects imports, `exec()`/`eval()`/`compile()`/`__import__()`/`getattr()`/`setattr()` calls, global/nonlocal statements + - Added `_validate_lambda_ast()` helper: restricts transform `eval()` to lambda-only expressions via AST parsing + - Suppressed `0.0.0.0` bind default (`# nosec B104`) — intentional, configurable via `CLEVERAGENTS_SERVER_HOST` +- **Code quality** (LOW): + - Replaced 6 `assert` statements with proper `if`/`raise` (TypeError, RuntimeError, typer.BadParameter) — asserts stripped in optimized bytecode + - Replaced `try/except/pass` with `contextlib.suppress(Exception)` (2 locations in dispose()) + - Added logging to previously-silent exception handlers (migration_runner, nodes retry loop) + - Suppressed false positive `"token_count": 0` flagged as hardcoded password (`# nosec B105`) +- **Files modified**: `stream_router.py`, `yaml_template_engine.py`, `settings.py`, `context_service.py`, `memory_service.py`, `retry_patterns.py`, `context.py` (CLI), `plan_service.py`, `migration_runner.py`, `nodes.py` +- **Verification**: `bandit -r src/ -c pyproject.toml` → 0 findings; targeted behave tests pass; smoke tests for AST validation pass + +**2026-02-09**: Stages Q0, Q1, Q2 Complete - Full Quality Automation Setup [Brent] + +**Stage Q0 - Pre-commit Hooks:** + +- Created `.pre-commit-config.yaml` with 12 hooks across 5 categories: + - Branch protection: `no-commit-to-branch` (prevents commits to main) + - General checks: `check-yaml`, `check-toml`, `check-json`, `check-merge-conflict`, `check-added-large-files`, `end-of-file-fixer`, `trailing-whitespace`, `debug-statements` + - Ruff: `ruff-format` (auto-fix), `ruff` (lint with safe auto-fix) + - Pyright: local system hook running type checking on `src/` only + - Bandit: security scanning with `pyproject.toml` configuration on `src/` only + - Vulture: dead code detection with whitelist at `vulture_whitelist.py` + - Semgrep: custom rules in `.semgrep.yml` for eval/exec/os.system/pickle detection (graceful skip when not installed) + - Commitizen: conventional commit message validation at commit-msg stage +- Added dev dependencies to `pyproject.toml`: `pre-commit>=3.6.0`, `bandit[toml]>=1.7.5`, `vulture>=2.10`, `radon>=6.0.1` +- Added `[tool.bandit]` and `[tool.vulture]` sections to `pyproject.toml` +- Created `vulture_whitelist.py` for false positive suppression (exc_tb, build_data) +- Created `.semgrep.yml` with 5 custom security rules +- Created `scripts/setup-dev.sh` for developer environment setup +- Added 4 new nox sessions: `pre_commit`, `security_scan`, `dead_code`, `complexity` +- **Discovery**: CI platform is Forgejo (`.forgejo/`), NOT GitHub. Stage Q1 must use Forgejo Actions, not GitHub Actions. +- **Discovery**: Pre-existing security findings in production code need remediation (see Q0.8 spawned task) +- **Discovery**: 37 source files have formatting issues, ~27 files have trailing whitespace - pre-existing debt +- **Discovery**: `features/steps/actor_cli_steps.py` has many F811 (redefined step_impl) violations - Behave pattern +- **Discovery**: Average code complexity is A (3.56) across 979 blocks - good baseline +- **Discovery**: High complexity methods identified: `LegacyDataMigrator.migrate_project_data` E(37), `ProviderRegistry._create_provider_llm` C(20), `ProviderRegistry.create_ai_provider` C(18) +- Key files: `.pre-commit-config.yaml`, `.semgrep.yml`, `vulture_whitelist.py`, `scripts/setup-dev.sh` + +**Stage Q1 - CI/CD Pipeline:** + +- Extended `.forgejo/workflows/ci.yml` with 3 new jobs: + - `security`: bandit scan (JSON report + high-severity gate) + vulture dead code detection + - `quality`: radon complexity check (grade F fails build) + JSON report + - `coverage`: behave tests with coverage measurement, fail-under=85%, XML artifact +- Updated `docker` and `helm` jobs to depend on `security` (fail-fast on security issues) +- Created `scripts/check-quality-gates.py` aggregating: coverage, typecheck, security, dead code, complexity +- All reports uploaded as artifacts for downstream consumption + +**Stage Q2 - Advanced Automation:** + +- Created `.forgejo/workflows/nightly-quality.yml` for nightly quality monitoring: + - Runs at midnight UTC (cron: "0 0 \* \* \*") + manual trigger support + - Full lint, typecheck, security scan (all severities), dead code, complexity analysis + - Behave tests with coverage measurement + - Quality trend JSON generation with timestamp + metrics + - 90-day artifact retention for trend analysis +- Created `scripts/check-adr-compliance.py` with AST-based checks for: + - ADR-002: No threading imports in application layer + - ADR-003: Services use constructor dependency injection + - ADR-007: No direct SQLAlchemy usage in service layer +- Added `nox -s adr_compliance` session +- Created `.forgejo/pull_request_template.md` with quality checklist +- Created `docs/development/quality-automation.md` with full documentation: + - Quick start, pre-commit hooks reference, CI jobs table, security scanning guide + - Complexity monitoring grades, quality gates, troubleshooting + +**New nox sessions added:** `pre_commit`, `security_scan`, `dead_code`, `complexity`, `adr_compliance` +**Total files created:** 9 new files +**Total files modified:** 3 files (pyproject.toml, noxfile.py, .forgejo/workflows/ci.yml) + +**2026-02-10**: Task 10B.4 - Quality Metrics Baseline Established [Brent] + +- Ran full quality suite via nox to establish current baseline: + - **Unit Tests**: 105 features, 1613 scenarios, 7555 steps - ALL PASS + - **Lint (ruff)**: 0 findings + - **Typecheck (pyright)**: 0 errors, 0 warnings + - **Security (bandit)**: 0 findings (0 HIGH, 0 MEDIUM, 0 LOW) + - **Dead Code (vulture)**: 0 findings + - **Complexity (radon)**: Average A (3.56), 981 blocks analyzed, no grade-F methods + - High complexity methods to monitor: `LegacyDataMigrator.migrate_project_data` E(37), `Action.validate_arguments` C(20), `ProviderRegistry._create_provider_llm` C(20), `ProviderRegistry.create_ai_provider` C(18), `Settings.resolve_provider_defaults` C(18) + - **Coverage**: 96% (9860 statements, 269 missing, 2852 branches, 213 branch-miss) +- Fixed pre-existing test failure: `plan_lifecycle_cli_coverage.feature` scenario "Plan lifecycle list shows project summaries" - Rich table column wrapping at narrow terminal widths caused `+1 more` text to be split across rows. Fixed by patching console width to 200 in test setup. +- Fixed missing dependency: added `langchain-anthropic>=0.2.0` to `pyproject.toml` (was imported in `src/cleveragents/providers/llm/anthropic_provider.py` but not declared) + +**2026-02-10**: Task Q1.5 Complete - Branch Protection Rules Documentation [Brent] + +- Created `docs/development/ci-cd.md` (224 lines) documenting: + - Branch protection rules for `master` (required status checks, review requirements, push/force-push/deletion blocks) + - Step-by-step Forgejo branch protection setup instructions + - Review priority matrix (P0: architecture/security, P1: algorithms, P2: features, P3: tests/docs) + - CI job dependency graph and quality gates summary table + - Nightly quality monitoring reference + - Local development workflow quick-reference +- Cross-references existing `docs/development/quality-automation.md` and `.forgejo/pull_request_template.md` +- Required CI checks documented: `lint`, `typecheck`, `security`, `quality`, `behave`, `coverage`, `build` +- Review requirement: 1 approving review, selective depth by priority matrix + +**2026-02-10**: Task 10C.1 Complete - Edge Case Test Scenarios [Brent] + +- Created `features/edge_case_plan_scenarios.feature` (26 scenarios, 141 steps) covering: + - **Concurrent plan execution** (6 scenarios): Duplicate strategize/execute/apply start attempts, concurrent complete+fail on same phase, two plans from same action, concurrent transitions on different plans + - **Resource conflict scenarios** (7 scenarios): Read-only file MODIFY failure, overlapping CREATE+MODIFY on same path, CREATE with None content, MOVE with missing source, DELETE of already-deleted file, file paths with spaces, deeply nested directory creation + - **Validation failure chains** (6 scenarios): Multiple simultaneous argument validation failures (missing + wrong type), unknown + missing arguments combined, empty plan description rejection, ACTION phase with processing state, invalid namespace characters, invalid name characters + - **Rollback edge cases** (7 scenarios): Partial apply failure (first change persists on disk, second unchanged), errored plan cannot restart, fail preserves error message, errored plan not terminal, cancel preserves phase, errored strategize rejects execute, failed strategize rejects complete +- Created `features/steps/edge_case_plan_steps.py` with step definitions for all 26 scenarios +- Verified no step name collisions with existing 104 feature files +- All quality gates pass: lint 0 findings, typecheck 0 errors, full suite 106 features / 1639 scenarios / 7696 steps ALL PASS + +**2026-02-10**: Task 10C.4 Complete - Validation Test Fixtures [Brent] + +- Created `features/validation_test_fixtures.feature` (34 scenarios, 81 steps) covering 6 validation domains: + - **AST security validation** (`_validate_code_ast`): 12 scenarios testing import/global/nonlocal/exec/eval/compile/**import**/getattr/setattr rejection, syntax errors, and safe code acceptance + - **Lambda AST validation** (`_validate_lambda_ast`): 4 scenarios testing valid lambda, non-lambda rejection, syntax errors, function call rejection + - **Python content sanitization** (`_sanitize_python_content`): 4 scenarios testing passthrough, code fence stripping, docstring wrapping, irrecoverable syntax (null byte) + - **Project model validation**: 6 scenarios testing invalid chars, slashes, exclamation, valid names, relative path resolution, empty name + - **Change list coercion** (`_coerce_change_list`): 4 scenarios testing empty list, mixed entries, non-list, non-change entry + - **ActionArgument parsing**: 4 scenarios testing too few parts, invalid type, invalid requirement, reserved keyword name +- Created `features/steps/validation_test_fixture_steps.py` with complete step definitions for all 34 scenarios +- Fixed step name collisions: renamed `I create a project with name` -> `I create a project fixture with name` and `the project path should be absolute` -> `the project fixture path should be absolute` to avoid conflicts with `database_integration_steps.py` and `domain_models_steps.py` +- Moved inline import (`ArgumentRequirement`, `ArgumentType`) to file-level per CONTRIBUTING.md rules +- Fixed irrecoverable syntax scenario: `"def broken("` is actually recoverable via docstring wrapping; replaced with null byte input which is truly irrecoverable +- All quality gates pass: lint 0 findings, typecheck 0 errors, full suite 107 features / 1673 scenarios / 7777 steps ALL PASS + **2026-02-06**: CRITICAL ARCHITECTURAL DECISION - Tool-Based Resource Modification - **REPLACED**: OutputParser/code fence parsing approach - **WITH**: Tool-based change tracking (modern approach used by Claude Code, Cursor, Aider) @@ -320,7 +482,7 @@ The following work from the previous implementation has been completed and will - Added MCP skill adapter (now C7.mcp): connect to external MCP servers - Replaced C4 "Multi-File ChangeSet Generation" with "Tool-Based Change Tracking" - Added SkillInvocationTracker and ToolCallRouter components -- **Rationale**: +- **Rationale**: - No parsing ambiguity (is this code or explanation?) - Each operation is explicit, typed, and trackable - Supports rollback (replay inverse of recorded changes) @@ -830,7 +992,7 @@ MERGE POINT: Day 8 - All tracks converge for MVP verification - Coverage must be >=97% - Brent signs off on quality - Jeff leads integration testing - + MERGE POINT DAY 8 - EXPLICIT COORDINATION TASKS: ├── [Jeff - 9:00 AM] M1.1: Run full `nox` test suite, collect failures ├── [Jeff - 10:00 AM] M1.2: Verify Plan persistence (A5) connects to CLI (A4) -- 2.52.0 From aa9f9c4d0b3017a8c10960a09b7e103eaa529915 Mon Sep 17 00:00:00 2001 From: khyari hamza Date: Thu, 12 Feb 2026 15:59:21 +0100 Subject: [PATCH 09/11] refactor: fix linting issues --- features/steps/project_config_model_steps.py | 1 - features/steps/project_model_steps.py | 3 +-- features/steps/resource_model_steps.py | 1 - src/cleveragents/domain/models/core/project.py | 4 ++-- src/cleveragents/domain/models/core/resource.py | 6 +++--- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/features/steps/project_config_model_steps.py b/features/steps/project_config_model_steps.py index 5730cf9a4..bcee4e3d6 100644 --- a/features/steps/project_config_model_steps.py +++ b/features/steps/project_config_model_steps.py @@ -7,7 +7,6 @@ from behave.runner import Context from cleveragents.domain.models.core.project import ContextConfig, ValidationConfig - # ValidationConfig Steps (B1.5) diff --git a/features/steps/project_model_steps.py b/features/steps/project_model_steps.py index 632868a30..6b194124a 100644 --- a/features/steps/project_model_steps.py +++ b/features/steps/project_model_steps.py @@ -8,11 +8,10 @@ from behave import given, then, when from behave.runner import Context from pydantic import ValidationError -from cleveragents.domain.models.core.project import ContextConfig, Project +from cleveragents.domain.models.core.project import Project from cleveragents.domain.models.core.resource import ( Resource, ResourceType, - SandboxStrategy, ) VALID_ULID = "01ARZ3NDEKTSV4RRFFQ69G5FAV" diff --git a/features/steps/resource_model_steps.py b/features/steps/resource_model_steps.py index 93a4b46b3..77f271a71 100644 --- a/features/steps/resource_model_steps.py +++ b/features/steps/resource_model_steps.py @@ -14,7 +14,6 @@ from cleveragents.domain.models.core.resource import ( SandboxStrategy, ) - # ResourceType Steps diff --git a/src/cleveragents/domain/models/core/project.py b/src/cleveragents/domain/models/core/project.py index ec8990b33..25a5e3cb7 100644 --- a/src/cleveragents/domain/models/core/project.py +++ b/src/cleveragents/domain/models/core/project.py @@ -298,7 +298,7 @@ class Project(BaseModel): ValueError: If the string doesn't contain exactly one '/'. """ parts = namespaced.split("/", 1) - if len(parts) != 2: # noqa: PLR2004 + if len(parts) != 2: raise ValueError(f"Expected 'namespace/name' format, got '{namespaced}'") return parts[0], parts[1] @@ -314,7 +314,7 @@ class Project(BaseModel): Returns: New Project instance with the resource added. """ - new_resources = list(self.resources) + [resource] + new_resources = [*list(self.resources), resource] return self.model_copy(update={"resources": new_resources}) def remove_resource(self, name: str) -> Project: diff --git a/src/cleveragents/domain/models/core/resource.py b/src/cleveragents/domain/models/core/resource.py index f268ebc13..e26b1d5d4 100644 --- a/src/cleveragents/domain/models/core/resource.py +++ b/src/cleveragents/domain/models/core/resource.py @@ -10,7 +10,7 @@ Based on implementation_plan.md (B1.2, B1.3, B1.4) and ADR-004 (Pydantic Validat from __future__ import annotations from datetime import datetime -from enum import Enum +from enum import StrEnum from pathlib import Path from typing import Any @@ -19,7 +19,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator ULID_PATTERN = r"^[0-9A-HJKMNP-TV-Z]{26}$" -class ResourceType(str, Enum): +class ResourceType(StrEnum): """Types of resources a project can reference. Each resource type maps to a category of external data source @@ -34,7 +34,7 @@ class ResourceType(str, Enum): CLOUD_INFRASTRUCTURE = "cloud_infrastructure" -class SandboxStrategy(str, Enum): +class SandboxStrategy(StrEnum): """Strategies for isolating resource modifications during plan execution. Sandboxing ensures that changes made during plan execution can be -- 2.52.0 From 99c29d5e660388a7e9c3c17fd903ebc5e6a75f54 Mon Sep 17 00:00:00 2001 From: khyari hamza Date: Thu, 12 Feb 2026 16:11:55 +0100 Subject: [PATCH 10/11] refactor: standardize Field defaults in project model classes --- .../domain/models/core/project.py | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/src/cleveragents/domain/models/core/project.py b/src/cleveragents/domain/models/core/project.py index 25a5e3cb7..60c93b57a 100644 --- a/src/cleveragents/domain/models/core/project.py +++ b/src/cleveragents/domain/models/core/project.py @@ -4,8 +4,6 @@ Includes ValidationConfig (B1.5), ContextConfig (B1.6), and the Project model (B Based on Phase 0 discovery, implementation_plan.md, and ADR-004 (Pydantic Validation). """ -from __future__ import annotations - import re from datetime import datetime from pathlib import Path @@ -37,21 +35,23 @@ class ValidationConfig(BaseModel): Implements B1.5 from the implementation plan. """ - test_command: str | None = Field(None, description="Command to run tests") - lint_command: str | None = Field(None, description="Command to run linting") + test_command: str | None = Field(default=None, description="Command to run tests") + lint_command: str | None = Field(default=None, description="Command to run linting") type_check_command: str | None = Field( - None, description="Command to run type checking" + default=None, description="Command to run type checking" + ) + build_command: str | None = Field( + default=None, description="Command to build the project" ) - build_command: str | None = Field(None, description="Command to build the project") custom_commands: dict[str, str] = Field( default_factory=dict, description="Custom named validation commands", ) timeout_seconds: int = Field( - 300, description="Timeout for each validation command in seconds" + default=300, description="Timeout for each validation command in seconds" ) fail_on_lint_error: bool = Field( - True, description="Whether lint errors should fail validation" + default=True, description="Whether lint errors should fail validation" ) model_config = ConfigDict( @@ -102,24 +102,27 @@ class ContextConfig(BaseModel): description="File patterns to ignore during indexing", ) include_patterns: list[str] | None = Field( - None, description="File patterns to include (None means include all)" + default=None, description="File patterns to include (None means include all)" ) max_file_size: int = Field( - 1_000_000, description="Maximum file size in bytes (1MB default)" + default=1_000_000, description="Maximum file size in bytes (1MB default)" + ) + max_files: int = Field( + default=100_000, description="Maximum number of files to index" ) - max_files: int = Field(100_000, description="Maximum number of files to index") indexing_strategy: str = Field( - "full_text", description="Indexing strategy: full_text, semantic, etc." + default="full_text", + description="Indexing strategy: full_text, semantic, etc.", ) chunking_policy: str = Field( - "smart", description="Chunking policy: smart, fixed, etc." + default="smart", description="Chunking policy: smart, fixed, etc." ) - chunk_size: int = Field(1000, description="Chunk size in tokens") + chunk_size: int = Field(default=1000, description="Chunk size in tokens") @field_validator("ignore_patterns") @classmethod def merge_default_ignore_patterns( - cls: type[ContextConfig], v: list[str] + cls: "type[ContextConfig]", v: list[str] ) -> list[str]: """Merge user-provided ignore patterns with defaults. @@ -141,15 +144,15 @@ class ContextConfig(BaseModel): class ProjectSettings(BaseModel): """Project-specific settings and configuration.""" - auto_build: bool = Field(False, description="Automatically build plans") - auto_apply: bool = Field(False, description="Automatically apply changes") + auto_build: bool = Field(default=False, description="Automatically build plans") + auto_apply: bool = Field(default=False, description="Automatically apply changes") confirm_apply: bool = Field( - True, description="Require confirmation before applying" + default=True, description="Require confirmation before applying" ) max_context_size: int = Field( - 52428800, description="Maximum context size in bytes (50MB)" + default=52428800, description="Maximum context size in bytes (50MB)" ) - default_model: str = Field("mock-gpt", description="Default AI model") + default_model: str = Field(default="mock-gpt", description="Default AI model") include_paths: list[str] = Field( default_factory=list, description="Relative include globs" ) @@ -166,10 +169,10 @@ class ProjectSettings(BaseModel): class ProjectStats(BaseModel): """Statistics for a project.""" - plans: int = Field(0, ge=0) - context_files: int = Field(0, ge=0) - changes: int = Field(0, ge=0) - current_plan: str | None = Field(None) + plans: int = Field(default=0, ge=0) + context_files: int = Field(default=0, ge=0) + changes: int = Field(default=0, ge=0) + current_plan: str | None = Field(default=None) model_config = ConfigDict( str_strip_whitespace=True, @@ -188,19 +191,21 @@ class Project(BaseModel): """ # Legacy fields (preserved for backward compatibility) - id: int | None = Field(None, description="Legacy integer project ID") + id: int | None = Field(default=None, description="Legacy integer project ID") path: Path = Field(..., description="Project root path") - settings: ProjectSettings = Field(default_factory=lambda: ProjectSettings()) # type: ignore - current_plan_id: int | None = Field(None) + settings: ProjectSettings = Field(default_factory=lambda: ProjectSettings()) + current_plan_id: int | None = Field(default=None) # B1.1b - Identity fields project_id: str | None = Field( - None, description="Unique ULID identifier", pattern=ULID_PATTERN + default=None, description="Unique ULID identifier", pattern=ULID_PATTERN ) name: str = Field(..., min_length=1, max_length=255) - namespace: str = Field("local", description="Project namespace for grouping") + namespace: str = Field( + default="local", description="Project namespace for grouping" + ) description: str | None = Field( - None, description="Human-readable project description" + default=None, description="Human-readable project description" ) # B1.1c - Categorization fields @@ -211,10 +216,10 @@ class Project(BaseModel): default_factory=list, description="Project resource references" ) validation_config: ValidationConfig | None = Field( - None, description="Validation command configuration" + default=None, description="Validation command configuration" ) context_config: ContextConfig = Field( - default_factory=ContextConfig, + default_factory=lambda: ContextConfig(), description="Context indexing and filtering configuration", ) @@ -224,7 +229,7 @@ class Project(BaseModel): @field_validator("name") @classmethod - def validate_name(cls: type[Project], v: str) -> str: + def validate_name(cls: "type[Project]", v: str) -> str: """Validate project name.""" if not v.replace("-", "").replace("_", "").replace(" ", "").isalnum(): raise ValueError( @@ -234,7 +239,7 @@ class Project(BaseModel): @field_validator("namespace") @classmethod - def validate_namespace(cls: type[Project], v: str) -> str: + def validate_namespace(cls: "type[Project]", v: str) -> str: """Validate namespace format and reject reserved names. Namespace must match pattern: 'local' or start with lowercase letter @@ -256,7 +261,7 @@ class Project(BaseModel): @field_validator("path") @classmethod - def validate_path(cls: type[Project], v: Path) -> Path: + def validate_path(cls: "type[Project]", v: Path) -> Path: """Ensure path is absolute.""" return v.resolve() @@ -302,7 +307,7 @@ class Project(BaseModel): raise ValueError(f"Expected 'namespace/name' format, got '{namespaced}'") return parts[0], parts[1] - def add_resource(self, resource: Resource) -> Project: + def add_resource(self, resource: Resource) -> "Project": """Return a new Project with the resource added. Since the model may be frozen or validated, this returns @@ -317,7 +322,7 @@ class Project(BaseModel): new_resources = [*list(self.resources), resource] return self.model_copy(update={"resources": new_resources}) - def remove_resource(self, name: str) -> Project: + def remove_resource(self, name: str) -> "Project": """Return a new Project with the named resource removed. Args: -- 2.52.0 From 80eeca540b2f03b50e0651af9d1c9b9c4cbfcc73 Mon Sep 17 00:00:00 2001 From: khyari hamza Date: Thu, 12 Feb 2026 16:18:34 +0100 Subject: [PATCH 11/11] feat: add scenarios for parsing namespaced project names and name validation --- features/project_config_model.feature | 9 +++ features/project_model.feature | 17 +++++ features/steps/project_config_model_steps.py | 10 +++ features/steps/project_model_steps.py | 66 ++++++++++++++++++++ 4 files changed, 102 insertions(+) diff --git a/features/project_config_model.feature b/features/project_config_model.feature index a9b2d86cc..6b530faf2 100644 --- a/features/project_config_model.feature +++ b/features/project_config_model.feature @@ -119,3 +119,12 @@ Feature: Project Configuration Models Scenario: Create a ContextConfig with custom chunk size Given a ContextConfig with chunk_size 500 Then the context chunk_size should be 500 + + # B1.6c - Duplicate ignore patterns are deduplicated + + Scenario: ContextConfig deduplicates ignore patterns that overlap with defaults + Given a ContextConfig with ignore patterns ".git/,node_modules/,custom/" + Then the context ignore_patterns should contain ".git/" + And the context ignore_patterns should contain "node_modules/" + And the context ignore_patterns should contain "custom/" + And the context ignore_patterns should not contain duplicate ".git/" diff --git a/features/project_model.feature b/features/project_model.feature index 162fb068c..49511dc2f 100644 --- a/features/project_model.feature +++ b/features/project_model.feature @@ -122,3 +122,20 @@ Feature: Project Domain Model Extensions Given a Project with project_id "01ARZ3NDEKTSV4RRFFQ69G5FAV" and name "my-project" When I get the resource named "nonexistent" from the project Then the retrieved resource should be none + + # B1.1h - parse_namespaced_name static method + + Scenario: parse_namespaced_name splits valid namespaced string + When I parse the project namespaced name "team_alpha/my-project" + Then the parsed project namespace should be "team_alpha" + And the parsed project name should be "my-project" + + Scenario: parse_namespaced_name rejects string without slash + When I try to parse the project namespaced name "no-slash-here" + Then a project namespaced name parse error should be raised + + # B1.1i - Name validation edge cases + + Scenario: Project rejects name with special characters + When I try to create a Project with invalid name "bad@name!" + Then a name validation error should be raised diff --git a/features/steps/project_config_model_steps.py b/features/steps/project_config_model_steps.py index bcee4e3d6..4af533eae 100644 --- a/features/steps/project_config_model_steps.py +++ b/features/steps/project_config_model_steps.py @@ -301,3 +301,13 @@ def step_check_include_pattern(context: Context, pattern: str) -> None: f"Expected '{pattern}' in include_patterns, " f"got {context.context_config.include_patterns}" ) + + +@then('the context ignore_patterns should not contain duplicate "{pattern}"') +def step_check_no_duplicate_ignore_pattern(context: Context, pattern: str) -> None: + """Verify ignore_patterns does not contain duplicates of the given pattern.""" + count = context.context_config.ignore_patterns.count(pattern) + assert count == 1, ( + f"Expected exactly 1 occurrence of '{pattern}' in ignore_patterns, " + f"found {count}: {context.context_config.ignore_patterns}" + ) diff --git a/features/steps/project_model_steps.py b/features/steps/project_model_steps.py index 6b194124a..6c119fb2e 100644 --- a/features/steps/project_model_steps.py +++ b/features/steps/project_model_steps.py @@ -293,3 +293,69 @@ def step_check_retrieved_resource_name(context: Context, expected: str) -> None: def step_check_retrieved_resource_none(context: Context) -> None: """Verify retrieved resource is None.""" assert context.retrieved_resource is None + + +# parse_namespaced_name steps + + +@when('I parse the project namespaced name "{namespaced}"') +def step_parse_project_namespaced_name(context: Context, namespaced: str) -> None: + """Parse a namespaced name string via Project.parse_namespaced_name.""" + namespace, name = Project.parse_namespaced_name(namespaced) + context.parsed_project_namespace = namespace + context.parsed_project_name = name + + +@when('I try to parse the project namespaced name "{namespaced}"') +def step_try_parse_project_namespaced_name(context: Context, namespaced: str) -> None: + """Attempt to parse an invalid namespaced name string.""" + context.error = None + try: + Project.parse_namespaced_name(namespaced) + except ValueError as e: + context.error = e + + +@then('the parsed project namespace should be "{expected}"') +def step_check_parsed_project_namespace(context: Context, expected: str) -> None: + """Verify parsed project namespace.""" + assert context.parsed_project_namespace == expected + + +@then('the parsed project name should be "{expected}"') +def step_check_parsed_project_name(context: Context, expected: str) -> None: + """Verify parsed project name.""" + assert context.parsed_project_name == expected + + +@then("a project namespaced name parse error should be raised") +def step_check_project_namespaced_name_error(context: Context) -> None: + """Verify a namespaced name parse error was raised.""" + assert context.error is not None, ( + "Expected a ValueError for invalid namespaced name but none was raised" + ) + + +# Name validation steps + + +@when('I try to create a Project with invalid name "{name}"') +def step_try_create_project_bad_name(context: Context, name: str) -> None: + """Attempt to create a Project with an invalid name.""" + context.error = None + try: + context.project = Project( + project_id=VALID_ULID, + name=name, + path=Path("/tmp/test"), + ) + except (ValidationError, ValueError) as e: + context.error = e + + +@then("a name validation error should be raised") +def step_check_name_error(context: Context) -> None: + """Verify a name validation error was raised.""" + assert context.error is not None, ( + "Expected a name validation error but none was raised" + ) -- 2.52.0