Feature: Consolidated Domain Models Combined scenarios from: domain_models, enums_coverage, invariant_models, namespaced_project_model, session_model, subplan_model, orguserconfig_coverage # ============================================================ # Originally from: domain_models.feature # Feature: Domain Models Validation # ============================================================ Scenario: Create valid Project model Given I have valid project data When I create a Project model Then the project model should be created successfully And the project name should be validated And the project path should be absolute Scenario: Project name validation Given I have project data with invalid name "123!@#" When I try to create a Project model Then a validation error should be raised And the error should mention "Name must be alphanumeric" Scenario: Create valid Plan model Given I have valid plan data When I create a Plan model Then the plan model should be created successfully And the plan status should be "pending" And the plan should have timestamps Scenario: Plan status transitions Given I have a plan with status "pending" When I update the plan status to "building" Then the updated plan status should be "building" And the updated_at timestamp should be updated Scenario: Create valid Context model Given I have valid context data When I create a Context model Then the context model should be created successfully And the context type should be "file" And the context should have a path Scenario: Create valid Change model Given I have valid change data When I create a Change model Then the change model should be created successfully And the change operation should be valid And the change should not be applied by default Scenario: ChangeSet statistics Given I have a changeset with multiple changes When I get the changeset statistics Then the stats should show correct counts And the stats should count creates, modifies, and deletes Scenario: Import auto-generated models When I import the auto-generated models Then all models should load without errors And the models should have proper Pydantic configuration # TODO: Uncomment when step definitions are implemented # Scenario: Create an AIModel with default values # Given I create an "AIModel" with the following data: # | name | value | # | provider_id | test | # | model_id | test | # Then the "AIModel" object should have the following attributes: # | name | value | # | provider_id | test | # | model_id | test | # | is_chat | False | # | is_image_to_text | False | # | is_multi_modal | False | # | is_fine_tunable | False | # | is_function_calling | False | # # TODO: Uncomment when step definitions are implemented # Scenario: Create a Provider with default values # Given I create a "Provider" with the following data: # | name | value | # | provider_id | test | # | name | test | # Then the "Provider" object should have the following attributes: # | name | value | # | provider_id | test | # | name | test | # # TODO: Uncomment when step definitions are implemented # Scenario: Create a CustomAIModel with default values # Given I create a "CustomAIModel" with the following data: # | name | value | # | provider_id | test | # | model_id | test | # Then the "CustomAIModel" object should have the following attributes: # | name | value | # | provider_id | test | # | model_id | test | # | is_chat | False | # | is_image_to_text | False | # | is_multi_modal | False | # | is_fine_tunable | False | # | is_function_calling | False | Scenario: Context update result aggregates statistics Given I have context update statistics When I create a ContextUpdateResult summary Then the context update summary should report correct totals And the token diffs should remain non-negative Scenario: Context update summary parameters accept camelCase payloads Given I have summary counts for context updates When I create a SummaryForUpdateContextParams model Then the summary counts should match the source data Scenario: Create cloud billing fields from camelCase payload Given I have sample cloud billing data When I create a CloudBillingFields model Then the billing fields should expose decimal values Scenario: Create organization with billing fields Given I have organization data with billing When I create an Org domain model Then the org should include billing configuration Scenario: Create a user with default plan config Given I have user data referencing a default plan config When I create a User model Then the user should include the default plan config Scenario: Create an org role definition Given I have an org role definition When I create an OrgRole model Then the org role should retain its label Scenario: Create an org user association Given I have an org user association When I create an OrgUser model Then the org user config should be attached Scenario: Create an invite record with timestamps Given I have invite metadata When I create an Invite model Then the invite should track acceptance timestamps Scenario: Create a credits transaction entry Given I have credits transaction data When I create a CreditsTransaction model Then the credits transaction should track balances # ============================================================ # Originally from: enums_coverage.feature # Feature: Domain Enums Coverage # ============================================================ @unit @enums Scenario: ModelPublisher enum has all expected values Given I import the ModelPublisher enum When I check all ModelPublisher values Then ModelPublisher.OPENAI should equal "ModelPublisherOpenAI" And ModelPublisher.ANTHROPIC should equal "ModelPublisherAnthropic" And ModelPublisher.GOOGLE should equal "ModelPublisherGoogle" And ModelPublisher.DEEPSEEK should equal "ModelPublisherDeepSeek" And ModelPublisher.PERPLEXITY should equal "ModelPublisherPerplexity" And ModelPublisher.QWEN should equal "ModelPublisherQwen" And ModelPublisher.MISTRAL should equal "ModelPublisherMistral" @unit @enums Scenario: ModelPublisher enum is string-based Given I have a ModelPublisher enum value When I convert ModelPublisher.OPENAI to string Then it should be a string type And it should equal "ModelPublisherOpenAI" @unit @enums Scenario: ModelPublisher enum membership check Given I import the ModelPublisher enum When I check enum membership Then "ModelPublisherOpenAI" should be a valid ModelPublisher value And "InvalidPublisher" should not be a valid ModelPublisher value @unit @enums Scenario: ModelPublisher enum iteration Given I import the ModelPublisher enum When I iterate over all ModelPublisher values Then I should get 7 enum members And each member should be a ModelPublisher instance @unit @enums Scenario: ModelErrKind enum has all expected values Given I import the ModelErrKind enum When I check all ModelErrKind values Then ModelErrKind.OVERLOADED should equal "ErrOverloaded" And ModelErrKind.CONTEXT_TOO_LONG should equal "ErrContextTooLong" And ModelErrKind.RATE_LIMITED should equal "ErrRateLimited" And ModelErrKind.SUBSCRIPTION_QUOTA_EXHAUSTED should equal "ErrSubscriptionQuotaExhausted" And ModelErrKind.OTHER should equal "ErrOther" And ModelErrKind.CACHE_SUPPORT should equal "ErrCacheSupport" @unit @enums Scenario: ModelErrKind enum is string-based Given I have a ModelErrKind enum value When I convert ModelErrKind.RATE_LIMITED to string Then it should be a string type And it should equal "ErrRateLimited" @unit @enums Scenario: ModelErrKind enum membership check Given I import the ModelErrKind enum When I check enum membership Then "ErrOverloaded" should be a valid ModelErrKind value And "InvalidError" should not be a valid ModelErrKind value @unit @enums Scenario: ModelErrKind enum iteration Given I import the ModelErrKind enum When I iterate over all ModelErrKind values Then I should get 6 enum members And each member should be a ModelErrKind instance @unit @enums Scenario: FallbackType enum has all expected values Given I import the FallbackType enum When I check all FallbackType values Then FallbackType.ERROR should equal "FallbackTypeError" And FallbackType.CONTEXT should equal "FallbackTypeContext" And FallbackType.PROVIDER should equal "FallbackTypeProvider" @unit @enums Scenario: FallbackType enum is string-based Given I have a FallbackType enum value When I convert FallbackType.ERROR to string Then it should be a string type And it should equal "FallbackTypeError" @unit @enums Scenario: FallbackType enum membership check Given I import the FallbackType enum When I check enum membership Then "FallbackTypeError" should be a valid FallbackType value And "InvalidFallback" should not be a valid FallbackType value @unit @enums Scenario: FallbackType enum iteration Given I import the FallbackType enum When I iterate over all FallbackType values Then I should get 3 enum members And each member should be a FallbackType instance @unit @enums Scenario: ModelProvider enum has all expected values Given I import the ModelProvider enum When I check all ModelProvider values Then ModelProvider.OPENROUTER should equal "ModelProviderOpenRouter" And ModelProvider.OPENAI should equal "ModelProviderOpenAI" And ModelProvider.ANTHROPIC should equal "ModelProviderAnthropic" And ModelProvider.ANTHROPIC_CLAUDE_MAX should equal "ModelProviderAnthropicClaudeMax" And ModelProvider.GOOGLE_AI_STUDIO should equal "ModelProviderGoogleAIStudio" And ModelProvider.GOOGLE_VERTEX should equal "ModelProviderGoogleVertex" And ModelProvider.AZURE_OPENAI should equal "ModelProviderAzureOpenAI" And ModelProvider.DEEPSEEK should equal "ModelProviderDeepSeek" And ModelProvider.PERPLEXITY should equal "ModelProviderPerplexity" And ModelProvider.AMAZON_BEDROCK should equal "ModelProviderAmazonBedrock" @unit @enums Scenario: ModelProvider enum is string-based Given I have a ModelProvider enum value When I convert ModelProvider.OPENAI to string Then it should be a string type And it should equal "ModelProviderOpenAI" @unit @enums Scenario: ModelProvider enum membership check Given I import the ModelProvider enum When I check enum membership Then "ModelProviderOpenAI" should be a valid ModelProvider value And "InvalidProvider" should not be a valid ModelProvider value @unit @enums Scenario: ModelProvider enum iteration Given I import the ModelProvider enum When I iterate over all ModelProvider values Then I should get 10 enum members And each member should be a ModelProvider instance @unit @enums Scenario: Enum value comparison Given I have ModelPublisher enum values When I compare enum values Then ModelPublisher.OPENAI should equal ModelPublisher.OPENAI And ModelPublisher.OPENAI should not equal ModelPublisher.ANTHROPIC @unit @enums Scenario: Enum name attribute access Given I import the ModelPublisher enum When I access the name attribute of ModelPublisher.OPENAI Then the name should be "OPENAI" @unit @enums Scenario: Enum value attribute access Given I import the ModelPublisher enum When I access the value attribute of ModelPublisher.OPENAI Then the value should be "ModelPublisherOpenAI" @unit @enums Scenario: Creating enum from string value Given I import the ModelProvider enum When I create ModelProvider from value "ModelProviderOpenAI" Then I should get ModelProvider.OPENAI @unit @enums Scenario: Enum hash consistency Given I have ModelErrKind enum values When I use enum values as dictionary keys Then the enum values should work as dictionary keys And the hash should be consistent @unit @enums Scenario: Enum string representation Given I have a FallbackType enum value When I get the string representation Then repr should contain "FallbackType.ERROR" And str should equal "FallbackTypeError" # ============================================================ # Originally from: invariant_models.feature # Feature: Invariant Models and Enforcement # ============================================================ Scenario: Create a global invariant When I create an invariant with text "Never delete production data" scope "global" and source "system" Then the invariant should be created And the invariant scope should be "global" And the invariant source name should be "system" And the invariant should be active Scenario: Create a project invariant When I create an invariant with text "All API changes need tests" scope "project" and source "myapp" Then the invariant should be created And the invariant scope should be "project" And the invariant source name should be "myapp" Scenario: Create an action invariant When I create an invariant with text "Minimum 80% coverage" scope "action" and source "local/code-coverage" Then the invariant should be created And the invariant scope should be "action" Scenario: Create a plan invariant When I create an invariant with text "Use Python 3.13 only" scope "plan" and source "PLAN123" Then the invariant should be created And the invariant scope should be "plan" Scenario: Invariant gets a ULID id When I create an invariant with text "Test constraint" scope "global" and source "system" Then the invariant id should be a valid ULID Scenario: Invariant text is stripped of whitespace When I create an invariant with text " padded text " scope "global" and source "system" Then the invariant text should be "padded text" # === Empty Invariant Text Rejection === Scenario: Empty invariant text is rejected When I try to create an invariant with empty text Then a validation error should be raised And the error should mention "at least 1 character" Scenario: Whitespace-only invariant text is rejected When I try to create an invariant with whitespace-only text Then a validation error should be raised # === InvariantScope Enum === Scenario: InvariantScope has four values Then InvariantScope should have values "global, project, action, plan" # === Merge Precedence (plan > action > project > global) === Scenario: Merge with no duplicates preserves all invariants Given I have plan invariants | text | source | | Plan constraint | plan1 | And I have project invariants | text | source | | Project constraint | proj1 | And I have global invariants | text | source | | Global constraint | system | When I merge the invariants Then the merged set should have 3 invariants And the merged invariant at index 0 should have text "Plan constraint" And the merged invariant at index 1 should have text "Project constraint" And the merged invariant at index 2 should have text "Global constraint" Scenario: Plan invariant overrides project invariant with same text Given I have plan invariants | text | source | | Log all changes | plan1 | And I have project invariants | text | source | | Log all changes | proj1 | And I have global invariants | text | source | When I merge the invariants Then the merged set should have 1 invariants And the merged invariant at index 0 should have scope "plan" Scenario: Project invariant overrides global invariant with same text Given I have plan invariants | text | source | And I have project invariants | text | source | | Log all changes | proj1 | And I have global invariants | text | source | | Log all changes | system | When I merge the invariants Then the merged set should have 1 invariants And the merged invariant at index 0 should have scope "project" Scenario: Merge precedence is case-insensitive for de-duplication Given I have plan invariants | text | source | | LOG ALL CHANGES | plan1 | And I have global invariants | text | source | | log all changes | system | And I have project invariants | text | source | When I merge the invariants Then the merged set should have 1 invariants And the merged invariant at index 0 should have text "LOG ALL CHANGES" Scenario: Merge with all four scopes preserves action tier — Issue #9126 Given I have plan invariants | text | source | | Plan rule | plan1 | And I have action invariants | text | source | | Action rule | action1 | And I have project invariants | text | source | | Project rule | proj1 | And I have global invariants | text | source | | Global rule | system | When I merge the invariants Then the merged set should have 4 invariants And plan invariants appear before action invariants in merge And action invariants appear before project invariants in merge And the merged invariant at index 0 should have text "Plan rule" And the merged invariant at index 1 should have text "Action rule" And the merged invariant at index 2 should have text "Project rule" And the merged invariant at index 3 should have text "Global rule" Scenario: Action invariant overrides project with same text Given I have plan invariants | text | source | And I have action invariants | text | source | | Log all changes | action1 | And I have project invariants | text | source | | Log all changes | proj1 | And I have global invariants | text | source | When I merge the invariants Then the merged set should have 1 invariants And the merged invariant at index 0 should have scope "action" Scenario: Action invariant is included in de-duplication with plan override Given I have plan invariants | text | source | | Log all changes | plan1 | And I have action invariants | text | source | | Log all changes | action1 | And I have project invariants | text | source | And I have global invariants | text | source | When I merge the invariants Then the merged set should have 1 invariants And the merged invariant at index 0 should have scope "plan" Scenario: Inactive action invariants are excluded from merge Given I have plan invariants with an inactive entry And I have action invariants | text | source | | Active | action1 | And I have project invariants | text | source | And I have global invariants | text | source | When I merge the invariants Then the merged set should have 1 invariants And the merged invariant at index 0 should have scope "action" Scenario: Inactive invariants are excluded from merge Given I have plan invariants with an inactive entry And I have project invariants | text | source | And I have global invariants | text | source | When I merge the invariants Then the merged set should have 0 invariants # === InvariantSet merge class method === Scenario: InvariantSet.merge produces correct result Given I have plan invariants | text | source | | Plan rule | plan1 | And I have project invariants | text | source | | Project rule | proj1 | And I have global invariants | text | source | | Global rule | system | When I merge using InvariantSet Then the invariant set should have 3 invariants Scenario: InvariantSet.merge with four-tier precedence — Issue #9126 Given I have plan invariants | text | source | | Plan rule | plan1 | And I have action invariants | text | source | | Action rule | action1 | And I have project invariants | text | source | | Project rule | proj1 | And I have global invariants | text | source | | Global rule | system | When I merge using InvariantSet Then the invariant set should have 4 invariants # === Service: Add/List/Remove === Scenario: Service add invariant Given an invariant service When I add an invariant via service with text "No secrets in logs" scope "global" source "system" Then the service should return the created invariant And the service invariant text should be "No secrets in logs" Scenario: Service add invariant rejects empty text Given an invariant service When I try to add an invariant via service with empty text Then a service validation error should be raised Scenario: Service add invariant rejects empty source Given an invariant service When I try to add an invariant via service with empty source Then a service validation error should be raised Scenario: Service list all invariants Given an invariant service with 3 invariants When I list all invariants via service Then the service should return 3 invariants Scenario: Service list invariants filtered by scope Given an invariant service with mixed scopes When I list invariants via service with scope "global" Then all returned invariants should have scope "global" Scenario: Service list invariants filtered by source Given an invariant service with mixed sources When I list invariants via service with source "myapp" Then all returned invariants should have source "myapp" Scenario: Service remove invariant soft-deletes Given an invariant service with 1 invariant When I remove the invariant via service Then the invariant should be inactive And listing active invariants should return 0 Scenario: Service remove non-existent invariant raises not found Given an invariant service When I try to remove a non-existent invariant Then a not found error should be raised Scenario: Service remove with empty ID raises validation error Given an invariant service When I try to remove an invariant with empty ID Then a service validation error should be raised # === Effective Invariant Computation === Scenario: Effective invariants for a plan with project context Given an invariant service with invariants at all scopes When I get effective invariants for plan "plan1" and project "proj1" Then the effective set should contain plan invariants first And the effective set should contain project invariants second And the effective set should contain global invariants last Scenario: Effective invariants include action scope — Issue #9126 Given an invariant service with invariants at all four scopes When I get effective invariants for plan "plan1", action "action1", and project "proj1" Then the effective set should contain plan invariants first And the effective set should contain action invariants second And the effective set should contain project invariants after action And the effective set should contain global invariants last And the effective set should have 4 invariants Scenario: Effective invariants de-duplicate across scopes Given an invariant service with duplicate text across scopes When I get effective invariants for plan "plan1" and project "proj1" Then the effective set should have no duplicates # === Enforcement Record Creation === Scenario: Enforce invariants creates records Given an invariant service with 2 invariants When I enforce invariants for plan "PLAN_XYZ" Then 2 enforcement records should be created And each enforcement record should be marked as enforced And each enforcement record should have a decision ID Scenario: Enforce invariants with actor response Given an invariant service with 1 invariant When I enforce invariants with actor response "All constraints satisfied" Then the enforcement record actor response should be "All constraints satisfied" Scenario: Enforce invariants with empty plan ID raises error Given an invariant service When I try to enforce invariants with empty plan ID Then a service validation error should be raised # === InvariantViolation Model === Scenario: Create an invariant violation When I create an invariant violation with severity "error" Then the violation should be created And the invariant violation severity should be "error" Scenario: Violation severity is case-insensitive When I create an invariant violation with severity "WARNING" Then the invariant violation severity should be "warning" Scenario: Invalid violation severity is rejected When I try to create a violation with invalid severity "critical" Then a validation error should be raised # === InvariantEnforcementRecord Model === Scenario: Create an enforcement record When I create an enforcement record with enforced true Then the record should be created And the record enforced flag should be true Scenario: Create an enforcement record with enforced false When I create an enforcement record with enforced false Then the record enforced flag should be false # ============================================================ # Immutability contract (frozen=True) — Issue #3116 # ============================================================ Scenario: Invariant model is immutable after creation When I create an invariant with text "Never delete production data" scope "global" and source "system" Then mutating the invariant active field should raise an error Scenario: InvariantViolation model is immutable after creation When I create an invariant violation with severity "error" Then mutating the violation severity field should raise an error Scenario: InvariantEnforcementRecord model is immutable after creation When I create an enforcement record with enforced true Then mutating the record enforced field should raise an error Scenario: Invariant model is hashable When I create an invariant with text "Hashable constraint" scope "global" and source "system" Then the invariant should be hashable Scenario: InvariantViolation model is hashable When I create an invariant violation with severity "warning" Then the violation should be hashable Scenario: InvariantEnforcementRecord model is hashable When I create an enforcement record with enforced true Then the record should be hashable Scenario: InvariantSet model is immutable after creation Given I have plan invariants | text | source | | Plan rule | plan1 | When I merge using InvariantSet Then mutating the invariant set invariants field should raise an error Scenario: InvariantSet model is hashable Given I have plan invariants | text | source | | Hashable rule | plan1 | When I merge using InvariantSet Then the invariant set should be hashable Scenario: Service soft-delete creates new instance with active=False Given an invariant service with 1 invariant When I remove the invariant via service Then the invariant should be inactive And listing active invariants should return 0 And the removed invariant is a different object than the original And the original invariant should still be active # ============================================================ # Originally from: namespaced_project_model.feature # Feature: Namespaced Project Domain Model # ============================================================ Scenario: Parse bare name defaults to local namespace When I parse the project namespaced name "my-project" Then the nsproject parsed namespace should be "local" And the nsproject parsed name should be "my-project" And the nsproject parsed server should be empty Scenario: Parse name with explicit namespace When I parse the project namespaced name "freemo/code-coverage" Then the nsproject parsed namespace should be "freemo" And the nsproject parsed name should be "code-coverage" And the nsproject parsed server should be empty Scenario: Parse name with server qualifier When I parse the project namespaced name "dev:freemo/code-coverage" Then the nsproject parsed server should be "dev" And the nsproject parsed namespace should be "freemo" And the nsproject parsed name should be "code-coverage" Scenario: Parse local namespace When I parse the project namespaced name "local/my-project" Then the nsproject parsed namespace should be "local" And the nsproject parsed name should be "my-project" Scenario: ParsedName qualified_name with server When I parse the project namespaced name "prod:myorg/api" Then the nsproject qualified name should be "prod:myorg/api" Scenario: ParsedName qualified_name without server When I parse the project namespaced name "myorg/api" Then the nsproject qualified name should be "myorg/api" Scenario: ParsedName namespaced_name When I parse the project namespaced name "dev:myorg/api" Then the nsproject namespaced name should be "myorg/api" Scenario: ParsedName is_local for bare name When I parse the project namespaced name "my-project" Then the nsproject parsed name should be local Scenario: ParsedName is_remote with server When I parse the project namespaced name "dev:myorg/api" Then the nsproject parsed name should be remote Scenario: ParsedName is_remote with non-local namespace When I parse the project namespaced name "freemo/api" Then the nsproject parsed name should be remote Scenario: Reject empty name When I try to parse an empty project namespaced name Then a nsproject validation error should be raised Scenario: Reject reserved namespace system When I try to parse the project namespaced name "system/my-project" Then a nsproject validation error should be raised Scenario: Reject reserved namespace admin When I try to parse the project namespaced name "admin/my-project" Then a nsproject validation error should be raised Scenario: Reject provider namespace openai When I try to parse the project namespaced name "openai/my-project" Then a nsproject validation error should be raised Scenario: Reject provider namespace anthropic When I try to parse the project namespaced name "anthropic/my-model" Then a nsproject validation error should be raised Scenario: Reject invalid characters in name When I try to parse the project namespaced name "my@project" Then a nsproject validation error should be raised # TemporalScope enum Scenario: TemporalScope has three values Then the nsproject TemporalScope enum should have values "current, recent, all" # ContextConfig model Scenario: ContextConfig defaults Given a default ContextConfig Then the nsproject ctx max_file_size should be 1000000 And the nsproject ctx max_total_size should be 52428800 And the nsproject ctx indexing_strategy should be "full_text" And the nsproject ctx chunking_policy should be "smart" And the nsproject ctx chunk_size should be 1000 And the nsproject ctx summarize should be true And the nsproject ctx auto_refresh should be true And the nsproject ctx temporal_scope should be "current" And the nsproject ctx retention_policy should be empty Scenario: ContextConfig merges default ignore patterns Given a ContextConfig with custom ignore patterns Then the nsproject ctx ignore patterns should include defaults And the nsproject ctx ignore patterns should include the custom pattern Scenario: ContextConfig with memory tier settings Given a ContextConfig with hot_max_tokens 4096 and warm_max_decisions 50 Then the nsproject ctx hot_max_tokens should be 4096 And the nsproject ctx warm_max_decisions should be 50 Scenario: ContextConfig is frozen Given a default ContextConfig When I try to mutate a ContextConfig field Then a nsproject validation error should be raised Scenario: ContextConfig rejects zero max_file_size When I try to create a ContextConfig with max_file_size 0 Then a nsproject validation error should be raised Scenario: ContextConfig rejects negative chunk_size When I try to create a ContextConfig with chunk_size -1 Then a nsproject validation error should be raised # LinkedResource model Scenario: Create a LinkedResource with defaults Given a LinkedResource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" Then the nsproject linked rid should be "01HZQX7K8M3N4P5R6S7T8V9W0X" And the nsproject linked read_only should be false And the nsproject linked alias should be empty And the nsproject linked_at should be set Scenario: Create a LinkedResource with alias Given a LinkedResource with id "01HZQX7K8M3N4P5R6S7T8V9W0X" and alias "main-repo" Then the nsproject linked alias should be "main-repo" Scenario: LinkedResource rejects invalid ULID When I try to create a LinkedResource with resource_id "invalid" Then a nsproject validation error should be raised Scenario: LinkedResource rejects empty alias When I try to create a LinkedResource with empty alias Then a nsproject validation error should be raised Scenario: LinkedResource is frozen Given a LinkedResource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" When I try to mutate a LinkedResource field Then a nsproject validation error should be raised # NamespacedProject model Scenario: Create a minimal NamespacedProject Given a NamespacedProject with name "my-project" Then the nsproject name should be "my-project" And the nsproject namespace should be "local" And the nsproject server should be empty And the nsproject namespaced name should be "local/my-project" And the nsproject qualified name should be "local/my-project" And the nsproject linked resources should be empty And the nsproject created_at should be set And the nsproject updated_at should be set Scenario: Create a NamespacedProject with namespace Given a project "api" under namespace "freemo" Then the nsproject namespace should be "freemo" And the nsproject namespaced name should be "freemo/api" Scenario: Create a NamespacedProject with server Given a project "api" under namespace "freemo" on server "dev" Then the nsproject server should be "dev" And the nsproject qualified name should be "dev:freemo/api" Scenario: NamespacedProject is_local Given a NamespacedProject with name "my-project" Then the nsproject should be local Scenario: NamespacedProject is_remote with namespace Given a project "api" under namespace "freemo" Then the nsproject should be remote Scenario: NamespacedProject is_remote with server Given a project "api" under namespace "freemo" on server "dev" Then the nsproject should be remote # NamespacedProject - validation Scenario: NamespacedProject rejects invalid name When I try to create a NamespacedProject with name "123invalid" Then a nsproject validation error should be raised Scenario: NamespacedProject rejects reserved namespace When I try to create a NamespacedProject with namespace "system" Then a nsproject validation error should be raised Scenario: NamespacedProject rejects provider namespace When I try to create a NamespacedProject with namespace "openai" Then a nsproject validation error should be raised Scenario: NamespacedProject rejects duplicate linked resources When I try to create a NamespacedProject with duplicate linked resources Then a nsproject validation error should be raised # NamespacedProject - domain methods Scenario: Link a resource to a project Given a NamespacedProject with name "my-project" When I link resource "01HZQX7K8M3N4P5R6S7T8V9W0X" to the project Then the nsproject should have 1 linked resource And the nsproject first linked resource_id should be "01HZQX7K8M3N4P5R6S7T8V9W0X" Scenario: Link a resource with read_only and alias Given a NamespacedProject with name "my-project" When I link resource "01HZQX7K8M3N4P5R6S7T8V9W0X" with read_only true and alias "docs" Then the nsproject should have 1 linked resource And the nsproject first linked resource should be read_only And the nsproject first linked resource alias should be "docs" Scenario: Unlink a resource from a project Given a NamespacedProject with name "my-project" And I link resource "01HZQX7K8M3N4P5R6S7T8V9W0X" to the project When I unlink resource "01HZQX7K8M3N4P5R6S7T8V9W0X" from the project Then the nsproject linked resources should be empty Scenario: Link duplicate resource fails Given a NamespacedProject with name "my-project" And I link resource "01HZQX7K8M3N4P5R6S7T8V9W0X" to the project When I try to link resource "01HZQX7K8M3N4P5R6S7T8V9W0X" again Then a nsproject validation error should be raised Scenario: Unlink non-existent resource fails Given a NamespacedProject with name "my-project" When I try to unlink resource "01HZQX7K8M3N4P5R6S7T8V9W0X" from the project Then a nsproject validation error should be raised Scenario: Get linked resource by resource_id Given a NamespacedProject with name "my-project" And I link resource "01HZQX7K8M3N4P5R6S7T8V9W0X" to the project When I get linked resource "01HZQX7K8M3N4P5R6S7T8V9W0X" from the project Then the nsproject retrieved link should not be empty Scenario: Get linked resource by alias Given a NamespacedProject with name "my-project" And I link resource "01HZQX7K8M3N4P5R6S7T8V9W0X" with read_only false and alias "main" When I get linked resource by alias "main" from the project Then the nsproject retrieved link should not be empty And the nsproject retrieved link alias should be "main" Scenario: Get non-existent linked resource returns None Given a NamespacedProject with name "my-project" When I get linked resource "01HZQX7K8M3N4P5R6S7T8V9W0X" from the project Then the nsproject retrieved link should be empty Scenario: Link resource with empty resource_id fails Given a NamespacedProject with name "my-project" When I try to link an empty resource_id to the project Then a nsproject validation error should be raised Scenario: Unlink resource with empty resource_id fails Given a NamespacedProject with name "my-project" When I try to unlink an empty resource_id from the project Then a nsproject validation error should be raised # ============================================================ # Originally from: session_model.feature # Feature: Session Domain Model # ============================================================ Scenario: Create a session with valid ULID When I create a session with a valid ULID Then the session model should be created And the session model session_id should be a valid ULID Scenario: Create a session with actor name When I create a session with actor name "local/orchestrator" Then the session model should be created And the session model actor_name should be "local/orchestrator" Scenario: Create a session without actor name When I create a session without actor name Then the session model should be created And the session model actor_name should be empty Scenario: Session has default namespace local When I create a session with a valid ULID Then the session model namespace should be "local" # ---- Session ID Validation ---- Scenario: Session rejects invalid ULID format When I try to create a session with invalid ID "not-a-ulid" Then a session model validation error should be raised # ---- Actor Name Validation ---- Scenario: Session rejects invalid actor name format When I try to create a session with invalid actor name "badformat" Then a session model validation error should be raised Scenario: Session accepts None actor name When I create a session without actor name Then the session model should be created # ---- Message Append with Auto-sequencing ---- Scenario: Append a user message to session Given a session with no messages When I append a user message "Hello" Then the session model should have 1 message And the session last message content should be "Hello" And the session last message role should be "user" And the session last message sequence should be 0 Scenario: Append multiple messages with auto-sequencing Given a session with no messages When I append a user message "Hello" And I append an assistant message "Hi there" Then the session model should have 2 messages And the session last message sequence should be 1 # ---- Message Role Validation ---- Scenario: Tool message requires tool_call_id When I try to create a tool message without tool_call_id Then a session model validation error should be raised And the session model error should mention "tool_call_id" Scenario: Tool message with tool_call_id is valid When I create a tool message with tool_call_id "call_123" Then the session message should be created And the session message tool_call_id should be "call_123" Scenario: User message does not require tool_call_id When I create a user message "Hello" Then the session message should be created # ---- Content Validation ---- Scenario: Message rejects whitespace-only content When I try to create a message with whitespace-only content Then a session model validation error should be raised And the session model error should mention "whitespace" Scenario: Message rejects empty content When I try to create a message with empty content Then a session model validation error should be raised # ---- Message Ordering ---- Scenario: Messages must be ordered by sequence When I try to create a session with out-of-order messages Then a session model validation error should be raised And the session model error should mention "ordered by sequence" # ---- Plan Linking ---- Scenario: Link a plan to session Given a session with no messages When I link plan "01ARZ3NDEKTSV4RRFFQ69G5FAV" to the session Then the session should have 1 linked plan Scenario: Plan linking deduplicates Given a session with no messages When I link plan "01ARZ3NDEKTSV4RRFFQ69G5FAV" to the session And I link plan "01ARZ3NDEKTSV4RRFFQ69G5FAV" to the session Then the session should have 1 linked plan Scenario: Link multiple plans Given a session with no messages When I link plan "01ARZ3NDEKTSV4RRFFQ69G5FAV" to the session And I link plan "01BX5ZZKBKACTAV9WEVGEMMVRY" to the session Then the session should have 2 linked plans # ---- Token Usage Tracking ---- Scenario: Default token usage is zero When I create a session with a valid ULID Then the session token usage input_tokens should be 0 And the session token usage output_tokens should be 0 And the session token usage estimated_cost should be 0.0 Scenario: Token usage with values When I create a session with token usage 100 input 50 output 0.05 cost Then the session token usage input_tokens should be 100 And the session token usage output_tokens should be 50 And the session token usage estimated_cost should be 0.05 Scenario: Token usage rejects negative values When I try to create token usage with negative input tokens Then a session model validation error should be raised # ---- Export Dict ---- Scenario: Export dict contains checksum Given a session with some messages When I export the session Then the export dict should have key "checksum" And the export dict should have key "schema_version" And the export dict should have key "messages" And the export dict should have key "session_id" Scenario: Export dict round-trip preserves data Given a session with some messages When I export the session Then the export dict messages count should match session message count # ---- CLI Dict ---- Scenario: CLI dict has required fields Given a session with some messages When I get the session model CLI dict Then the session cli dict should have key "session_summary" And the session cli dict should have key "token_usage" And the session cli dict session_summary should have key "id" And the session cli dict session_summary should have key "messages" And the session cli dict session_summary should have key "created" And the session cli dict session_summary should have key "updated" Scenario: CLI dict includes recent messages Given a session with some messages When I get the session model CLI dict Then the session cli dict should have key "recent_messages" And the session cli dict recent_messages text key should be "text" Scenario: CLI dict includes actor when set When I create a session with actor name "local/orchestrator" And I get the session model CLI dict Then the session cli dict session_summary should have key "actor" Scenario: CLI dict includes automation when set When I create a session with automation "review" And I get the session model CLI dict Then the session cli dict session_summary should have key "automation" And the session cli dict session_summary automation should be "review" Scenario: CLI dict linked_plans uses spec-compliant objects Given a session with linked plans When I get the session model CLI dict Then the session cli dict should have key "linked_plans" And the session cli dict linked_plans should contain plan_id field Scenario: CLI dict token_usage estimated_cost is formatted string Given a session with token usage cost 0.0184 When I get the session model CLI dict Then the session cli dict token_usage estimated_cost should be a string starting with "$" # ---- Empty Session Properties ---- Scenario: Empty session has zero message count Given a session with no messages Then the session message count should be 0 And the session is_empty should be true And the session last_message should be none # ---- get_messages with limit/offset ---- Scenario: get_messages with no limit returns all Given a session with 5 messages When I get messages with no limit Then I should get 5 messages Scenario: get_messages with limit Given a session with 5 messages When I get messages with limit 3 Then I should get 3 messages Scenario: get_messages with offset Given a session with 5 messages When I get messages with offset 2 Then I should get 3 messages Scenario: get_messages with limit and offset Given a session with 5 messages When I get messages with limit 2 and offset 1 Then I should get 2 messages # ---- Session Error Types ---- Scenario: SessionServiceError is base error Then SessionServiceError should be an Exception subclass Scenario: SessionNotFoundError inherits from SessionServiceError Then SessionNotFoundError should be a SessionServiceError subclass Scenario: SessionExportError inherits from SessionServiceError Then SessionExportError should be a SessionServiceError subclass Scenario: SessionImportError inherits from SessionServiceError Then SessionImportError should be a SessionServiceError subclass # ---- MessageRole enum ---- Scenario: MessageRole has expected values Then the MessageRole enum should have values "user,assistant,system,tool" # ---- Session metadata ---- Scenario: Session supports metadata When I create a session with metadata Then the session metadata should contain key "env" # ---- Export dict linked plans ---- Scenario: Export dict includes linked plan IDs Given a session with no messages When I link plan "01ARZ3NDEKTSV4RRFFQ69G5FAV" to the session And I export the session Then the export dict should have key "linked_plan_ids" # ---- Robot Smoke Alignment: Serialization Order ---- Scenario: Export dict keys are in expected serialization order Given a session with some messages When I export the session Then the export dict keys should be in expected serialization order Scenario: Export dict messages are ordered by sequence Given a session with some messages When I export the session Then the export dict messages should be ordered by sequence # ============================================================ # Originally from: subplan_model.feature # Feature: Subplan Model Hierarchy and Configuration # ============================================================ Scenario: Root plan has no parent and root equals self Given I create a subplan-test root plan Then the subplan-test plan should not be a subplan And the subplan-test plan should be a root plan And the subplan-test plan depth should be 0 Scenario: Root plan with explicit root_plan_id equal to plan_id Given I create a subplan-test plan with root_plan_id equal to plan_id Then the subplan-test plan should be a root plan And the subplan-test plan should not be a subplan # Child plan identity Scenario: Child plan with parent_plan_id set is a subplan Given I create a subplan-test child plan with a parent Then the subplan-test plan should be a subplan And the subplan-test plan should not be a root plan And the subplan-test plan attempt should be 1 Scenario: Root plan id propagates to nested children Given I create a subplan-test three-level hierarchy Then the subplan-test grandchild root_plan_id should match the root plan_id # Default values for new plans Scenario: New plan defaults to strategize phase and queued state Given I create a subplan-test default plan Then the subplan-test plan phase should be "strategize" And the subplan-test plan processing state should be "queued" And the subplan-test plan should not be terminal And the subplan-test plan should not be errored Scenario: New plan defaults to no automation profile Given I create a subplan-test default plan Then the subplan-test plan automation profile should be none Scenario: New plan has no subplan config by default Given I create a subplan-test default plan Then the subplan-test plan subplan_config should be none Scenario: New plan has empty subplan statuses by default Given I create a subplan-test default plan Then the subplan-test plan should have 0 subplan statuses # Subplan config storage and retrieval Scenario: Plan stores sequential subplan config Given I create a subplan-test plan with sequential subplan config Then the subplan-test plan subplan_config execution_mode should be "sequential" And the subplan-test plan subplan_config merge_strategy should be "git_three_way" And the subplan-test plan subplan_config fail_fast should be false Scenario: Plan stores parallel subplan config with custom settings Given I create a subplan-test plan with parallel subplan config Then the subplan-test plan subplan_config execution_mode should be "parallel" And the subplan-test plan subplan_config max_parallel should be 10 And the subplan-test plan subplan_config retry_failed should be true And the subplan-test plan subplan_config max_retries should be 3 Scenario: SubplanConfig defaults are sensible Given I create a subplan-test default subplan config Then the subplan-test subplan config execution_mode should be "sequential" And the subplan-test subplan config merge_strategy should be "git_three_way" And the subplan-test subplan config max_parallel should be 5 And the subplan-test subplan config fail_fast should be false And the subplan-test subplan config retry_failed should be true And the subplan-test subplan config max_retries should be 2 And the subplan-test subplan config timeout should be none # Plan hierarchy helpers Scenario: Plan with subplan statuses reports has_subplans true Given I create a subplan-test plan with one subplan status Then the subplan-test plan should have subplans Scenario: Plan without subplan statuses reports has_subplans false Given I create a subplan-test default plan Then the subplan-test plan should not have subplans Scenario: Depth returns negative one for non-root plans Given I create a subplan-test child plan with a parent Then the subplan-test plan depth should be -1 # Child plans follow same lifecycle as root plans Scenario: Child plan transitions follow standard lifecycle Then subplan-test transition from "action" to "strategize" should be valid And subplan-test transition from "strategize" to "execute" should be valid And subplan-test transition from "execute" to "apply" should be valid Scenario: Child plan invalid transitions are rejected Then subplan-test transition from "strategize" to "apply" should be invalid And subplan-test transition from "action" to "execute" should be invalid And subplan-test transition from "action" to "apply" should be invalid # Dependency guardrails Scenario: SubplanFailureHandler stops others when fail_fast is true Given I create a subplan-test fail-fast config with errored status Then the subplan-test failure handler should stop others Scenario: SubplanFailureHandler does not stop others for parallel without fail_fast Given I create a subplan-test parallel-no-fail-fast config with errored status Then the subplan-test failure handler should not stop others Scenario: SubplanFailureHandler retries retriable failure Given I create a subplan-test config with retriable timeout error Then the subplan-test failure handler should retry Scenario: SubplanFailureHandler does not retry non-retriable error Given I create a subplan-test config with non-retriable configuration error Then the subplan-test failure handler should not retry Scenario: SubplanFailureHandler does not retry when retries disabled Given I create a subplan-test config with retries disabled and retriable error Then the subplan-test failure handler should not retry Scenario: SubplanFailureHandler does not retry when max retries exceeded Given I create a subplan-test config with max retries exceeded Then the subplan-test failure handler should not retry # CLI dict rendering for subplan hierarchy Scenario: CLI dict includes parent_plan_id for child plans Given I create a subplan-test child plan with a parent And I build a subplan-test plan with the child identity Then the subplan-test cli dict should contain parent_plan_id Scenario: CLI dict includes subplan_count when subplans exist Given I create a subplan-test plan with one subplan status Then the subplan-test cli dict should contain subplan_count of 1 Scenario: CLI dict omits parent_plan_id for root plans Given I create a subplan-test root plan And I build a subplan-test plan with the root identity Then the subplan-test cli dict should not contain parent_plan_id # Validation guardrails Scenario: Invalid ULID for parent_plan_id raises validation error When I try to create a subplan-test identity with invalid parent ULID Then a validation error should be raised Scenario: SubplanConfig max_parallel below minimum raises validation error When I try to create a subplan-test config with max_parallel 0 Then a validation error should be raised Scenario: SubplanConfig max_parallel above maximum raises validation error When I try to create a subplan-test config with max_parallel 51 Then a validation error should be raised Scenario: SubplanConfig max_retries below minimum raises validation error When I try to create a subplan-test config with max_retries -1 Then a validation error should be raised # ============================================================ # Originally from: orguserconfig_coverage.feature # Feature: Org User Config Domain Model Coverage # ============================================================ Scenario: Import and instantiate OrgUserConfig with all fields Given the cleveragents package is available When I import OrgUserConfig from orguserconfig Then the OrgUserConfig class should be available And I can create an OrgUserConfig with all required fields Scenario: Create OrgUserConfig with prompted_claude_max field Given the cleveragents package is available When I create an OrgUserConfig with prompted_claude_max set to True Then the OrgUserConfig should have prompted_claude_max as True And the field should be accessible via the alias "promptedClaudeMax" Scenario: Create OrgUserConfig with use_claude_subscription field Given the cleveragents package is available When I create an OrgUserConfig with use_claude_subscription set to False Then the OrgUserConfig should have use_claude_subscription as False And the field should be accessible via the alias "useClaudeSubscription" Scenario: Create OrgUserConfig with claude_subscription_cooldown_started_at field Given the cleveragents package is available When I create an OrgUserConfig with a specific cooldown timestamp Then the OrgUserConfig should have the correct timestamp And the field should be accessible via the alias "claudeSubscriptionCooldownStartedAt" Scenario: Validate OrgUserConfig strips whitespace from string fields Given the cleveragents package is available When I create an OrgUserConfig with fields containing whitespace Then OrgUserConfig string fields should have whitespace stripped Scenario: Validate OrgUserConfig assignment validation Given the cleveragents package is available When I create an OrgUserConfig instance And I update its fields with new values for OrgUserConfig Then the OrgUserConfig assignment validation should be triggered And the OrgUserConfig values should be properly validated Scenario: Validate OrgUserConfig populate by name with camelCase aliases Given the cleveragents package is available When I create an OrgUserConfig using camelCase field aliases Then the OrgUserConfig fields should be populated correctly by name And both snake_case and camelCase access should work Scenario: Validate OrgUserConfig with dictionary input using aliases Given the cleveragents package is available When I create an OrgUserConfig from a dictionary with camelCase keys Then the model should correctly map aliased fields And all fields should be accessible with snake_case names Scenario: Test OrgUserConfig serialization with aliases Given the cleveragents package is available When I create an OrgUserConfig and serialize it Then the serialized output should use the defined aliases And the model should be deserializable from the serialized form Scenario: Test OrgUserConfig validation with invalid datetime Given the cleveragents package is available When I try to create an OrgUserConfig with an invalid datetime string Then an OrgUserConfig validation error should be raised And the error should indicate the datetime field Scenario: Test OrgUserConfig validation with missing required fields Given the cleveragents package is available When I try to create an OrgUserConfig without required fields Then an OrgUserConfig validation error should be raised And the error should list all missing required fields Scenario: Test OrgUserConfig model_config settings Given the cleveragents package is available When I inspect the OrgUserConfig model configuration Then OrgUserConfig str_strip_whitespace should be True And OrgUserConfig validate_assignment should be True And OrgUserConfig arbitrary_types_allowed should be False And OrgUserConfig populate_by_name should be True And OrgUserConfig use_enum_values should be True Scenario: Test OrgUserConfig with timezone-aware datetime Given the cleveragents package is available When I create an OrgUserConfig with a timezone-aware datetime Then the model should handle the timezone information correctly And the datetime should be properly stored Scenario: Test OrgUserConfig copy with update Given the cleveragents package is available When I create an OrgUserConfig and copy it with updates Then the OrgUserConfig copy should have the updated values And the OrgUserConfig original should remain unchanged Scenario: Test OrgUserConfig model_dump with aliases Given the cleveragents package is available When I create an OrgUserConfig and call model_dump with by_alias=True Then the output should use camelCase aliases When I call model_dump with by_alias=False Then the output should use snake_case field names Scenario: Test OrgUserConfig model_dump_json Given the cleveragents package is available When I create an OrgUserConfig and call model_dump_json Then the JSON output should be valid And it should use the configured aliases by default Scenario: Test OrgUserConfig field types validation Given the cleveragents package is available When I try to create an OrgUserConfig with wrong field types Then validation errors should be raised for type mismatches And the OrgUserConfig errors should clearly indicate the expected types Scenario: Test OrgUserConfig __init__ coverage Given the cleveragents package is available When I import the orguserconfig __init__ module Then the module should be loaded successfully And the OrgUserConfig class should be importable from it