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

1"""Memory garden and quarantine models.""" 

2 

3from __future__ import annotations 

4 

5from pydantic import BaseModel, Field, field_validator 

6 

7from .constants import VALID_GARDEN_ROLES, VALID_SCOPES 

8 

9 

10class GardenMemberRecord(BaseModel): 

11 entity_uri: str 

12 role: str 

13 added_by: str 

14 added_at: str 

15 

16 

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) 

28 

29 

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 ) 

39 

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 

46 

47 

48class GardenMemberRequest(BaseModel): 

49 entity_uri: str = Field(..., min_length=1) 

50 role: str = Field("reader") 

51 

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 

58 

59 

60class GardenMemberUpdateRequest(BaseModel): 

61 role: str 

62 

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 

69 

70 

71# --------------------------------------------------------------------------- 

72# Quarantine garden action models (spec §19.5.5, §5.25) 

73# --------------------------------------------------------------------------- 

74 

75 

76class QuarantinePromoteRequest(BaseModel): 

77 """POST /v1/gardens/:id/promote — move a quarantined fact to a target garden.""" 

78 

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.") 

85 

86 

87class QuarantineRejectRequest(BaseModel): 

88 """POST /v1/gardens/:id/reject — permanently reject a quarantined fact.""" 

89 

90 fact_id: str = Field(..., min_length=1) 

91 reason: str = Field("", description="Human-readable reason for rejection.") 

92 

93 

94class QuarantineRecord(BaseModel): 

95 """Quarantined fact as returned by GET /v1/quarantine.""" 

96 

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 

109 

110 

111class QuarantineListResponse(BaseModel): 

112 items: list[QuarantineRecord] 

113 total: int 

114