Coverage for node / src / stigmem_node / models / gardens.py: 97%
71 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-25 01:49 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-25 01:49 +0000
1"""Memory garden and quarantine models."""
3from __future__ import annotations
5from pydantic import BaseModel, Field, field_validator
7from .constants import VALID_GARDEN_ROLES, VALID_SCOPES
10class GardenMemberRecord(BaseModel):
11 entity_uri: str
12 role: str
13 added_by: str
14 added_at: str
17class GardenRecord(BaseModel):
18 id: str
19 garden_id: str
20 slug: str
21 name: str
22 scope: str
23 description: str | None = None
24 created_by: str
25 created_at: str
26 members: list[GardenMemberRecord] = Field(default_factory=list)
27 quarantine: bool = False # v1.1: True for quarantine gardens (§19.5)
30class GardenCreateRequest(BaseModel):
31 slug: str = Field(..., min_length=1, max_length=63)
32 name: str = Field(..., min_length=1)
33 scope: str = Field("company")
34 description: str | None = None
35 quarantine: bool = Field(
36 False,
37 description="Create as a quarantine garden (Spec-08-Quarantine-Garden)",
38 )
40 @field_validator("scope")
41 @classmethod
42 def check_scope(cls, s: str) -> str:
43 if s not in VALID_SCOPES:
44 raise ValueError(f"scope must be one of {VALID_SCOPES}")
45 return s
48class GardenMemberRequest(BaseModel):
49 entity_uri: str = Field(..., min_length=1)
50 role: str = Field("reader")
52 @field_validator("role")
53 @classmethod
54 def check_role(cls, r: str) -> str:
55 if r not in VALID_GARDEN_ROLES:
56 raise ValueError(f"role must be one of {VALID_GARDEN_ROLES}")
57 return r
60class GardenMemberUpdateRequest(BaseModel):
61 role: str
63 @field_validator("role")
64 @classmethod
65 def check_role(cls, r: str) -> str:
66 if r not in VALID_GARDEN_ROLES: 66 ↛ 67line 66 didn't jump to line 67 because the condition on line 66 was never true
67 raise ValueError(f"role must be one of {VALID_GARDEN_ROLES}")
68 return r
71# ---------------------------------------------------------------------------
72# Quarantine garden action models (spec §19.5.5, §5.25)
73# ---------------------------------------------------------------------------
76class QuarantinePromoteRequest(BaseModel):
77 """POST /v1/gardens/:id/promote — move a quarantined fact to a target garden."""
79 fact_id: str = Field(..., min_length=1)
80 target_garden_id: str | None = Field(
81 None,
82 description="Target garden UUID or slug. Null = promote to no-garden (main fabric).",
83 )
84 reason: str = Field("", description="Human-readable reason for promotion.")
87class QuarantineRejectRequest(BaseModel):
88 """POST /v1/gardens/:id/reject — permanently reject a quarantined fact."""
90 fact_id: str = Field(..., min_length=1)
91 reason: str = Field("", description="Human-readable reason for rejection.")
94class QuarantineRecord(BaseModel):
95 """Quarantined fact as returned by GET /v1/quarantine."""
97 fact_id: str
98 entity: str
99 relation: str
100 source: str
101 quarantine_status: str
102 quarantine_garden_id: str | None
103 quarantine_reason: str | None
104 quarantine_acted_by: str | None
105 quarantine_acted_at: str | None
106 source_trust: float | None
107 received_from: str | None
108 timestamp: str
111class QuarantineListResponse(BaseModel):
112 items: list[QuarantineRecord]
113 total: int