f89c60595f
CI / lint (pull_request) Successful in 15s
CI / typecheck (pull_request) Successful in 26s
CI / security (pull_request) Successful in 21s
CI / quality (pull_request) Successful in 15s
CI / integration_tests (pull_request) Successful in 4m21s
CI / build (pull_request) Successful in 16s
CI / unit_tests (pull_request) Successful in 8m19s
CI / docker (pull_request) Successful in 40s
CI / coverage (pull_request) Successful in 6m29s
Implement the B1 (Project Data Models) domain models from scratch, aligned with docs/specification.md: - Resource model: ULID PK, PhysVirt enum (physical|virtual), SandboxStrategy enum (5 spec values), ResourceCapabilities, extensible resource_type_name, DAG relationships, frozen model - NamespacedProject model: identified solely by [[server:]namespace/]name, LinkedResource for project-resource links, ContextConfig with memory tiers/retention/temporal scope, domain methods (link/unlink/get) - parse_namespaced_name(): full namespace parsing with reserved/provider namespace validation, bare name defaulting to local/ - Extract legacy Project/ProjectSettings/ProjectStats to project_legacy.py - 81 Behave scenarios (233 steps), all passing - Lint (ruff), typecheck (pyright) clean - Full existing test suite (2336 scenarios) unaffected
259 lines
11 KiB
Gherkin
259 lines
11 KiB
Gherkin
Feature: Namespaced Project Domain Model
|
|
As a developer
|
|
I want spec-aligned project domain models
|
|
So that projects use namespaced names with linked resources
|
|
|
|
# parse_namespaced_name
|
|
|
|
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
|