@@ -52,42 +52,52 @@ pub struct PropertySpec {
5252 pub nars_floor : Option < ( u8 , u8 ) > ,
5353 /// What kind of value this property holds (LF-21).
5454 pub semantic_type : SemanticType ,
55+ /// GDPR / data-protection classification (LF-6 marking).
56+ /// Default = `Marking::Internal` (GDPR-safe baseline).
57+ /// Override per-predicate via `.with_marking(...)`.
58+ pub marking : Marking ,
5559}
5660
5761impl PropertySpec {
5862 /// Create a Required property spec. Default codec: Passthrough (Index).
5963 /// Default NARS floor: (128, 128) — moderate confidence required.
64+ /// Default marking: `Marking::Internal` (GDPR-safe).
6065 pub const fn required ( predicate : & ' static str ) -> Self {
6166 Self {
6267 predicate,
6368 kind : PropertyKind :: Required ,
6469 codec_route : CodecRoute :: Passthrough ,
6570 nars_floor : Some ( ( 128 , 128 ) ) ,
6671 semantic_type : SemanticType :: PlainText ,
72+ marking : Marking :: Internal ,
6773 }
6874 }
6975
7076 /// Create an Optional property spec. Caller must specify codec route.
7177 /// No NARS floor by default (absence doesn't escalate).
78+ /// Default marking: `Marking::Internal` (GDPR-safe).
7279 pub const fn optional ( predicate : & ' static str , codec_route : CodecRoute ) -> Self {
7380 Self {
7481 predicate,
7582 kind : PropertyKind :: Optional ,
7683 codec_route,
7784 nars_floor : None ,
7885 semantic_type : SemanticType :: PlainText ,
86+ marking : Marking :: Internal ,
7987 }
8088 }
8189
8290 /// Create a Free property spec. Default codec: CamPq (Argmax).
8391 /// No NARS floor (schema-free, always accepted).
92+ /// Default marking: `Marking::Internal` (GDPR-safe).
8493 pub const fn free ( predicate : & ' static str ) -> Self {
8594 Self {
8695 predicate,
8796 kind : PropertyKind :: Free ,
8897 codec_route : CodecRoute :: CamPq ,
8998 nars_floor : None ,
9099 semantic_type : SemanticType :: PlainText ,
100+ marking : Marking :: Internal ,
91101 }
92102 }
93103
@@ -96,6 +106,14 @@ impl PropertySpec {
96106 self
97107 }
98108
109+ /// Override the GDPR / data-protection marking for this predicate (LF-6).
110+ /// Default is `Marking::Internal`. SMB customer schema overrides:
111+ /// `iban` → Financial, `geburtsdatum` → Pii, etc.
112+ pub const fn with_marking ( mut self , marking : Marking ) -> Self {
113+ self . marking = marking;
114+ self
115+ }
116+
99117 /// Override the NARS truth floor.
100118 pub const fn with_nars_floor ( mut self , frequency : u8 , confidence : u8 ) -> Self {
101119 self . nars_floor = Some ( ( frequency, confidence) ) ;
@@ -456,6 +474,41 @@ mod tests {
456474 assert ! ( p. nars_floor. is_none( ) ) ;
457475 }
458476
477+ /// LF-6: every PropertySpec defaults to `Marking::Internal` (GDPR-safe).
478+ #[ test]
479+ fn property_spec_marking_defaults_to_internal ( ) {
480+ assert_eq ! ( PropertySpec :: required( "kdnr" ) . marking, Marking :: Internal ) ;
481+ assert_eq ! ( PropertySpec :: optional( "note" , CodecRoute :: CamPq ) . marking, Marking :: Internal ) ;
482+ assert_eq ! ( PropertySpec :: free( "free" ) . marking, Marking :: Internal ) ;
483+ }
484+
485+ /// SMB schema marking pattern: chain `with_marking` per predicate.
486+ #[ test]
487+ fn property_spec_with_marking_overrides ( ) {
488+ let iban = PropertySpec :: required ( "iban" ) . with_marking ( Marking :: Financial ) ;
489+ let dob = PropertySpec :: required ( "geburtsdatum" ) . with_marking ( Marking :: Pii ) ;
490+ let note = PropertySpec :: free ( "note" ) ; // stays Internal
491+
492+ assert_eq ! ( iban. marking, Marking :: Financial ) ;
493+ assert_eq ! ( dob. marking, Marking :: Pii ) ;
494+ assert_eq ! ( note. marking, Marking :: Internal ) ;
495+
496+ // Per-row fold (W-2): `most_restrictive` over a row's markings.
497+ let row_markings = [ iban. marking , dob. marking , note. marking ] ;
498+ assert_eq ! ( Marking :: most_restrictive( & row_markings) , Marking :: Financial ) ;
499+ }
500+
501+ /// `with_marking` is const and chains with `with_semantic_type` (LF-21).
502+ #[ test]
503+ fn property_spec_with_marking_chains_with_semantic_type ( ) {
504+ const SPEC : PropertySpec = PropertySpec :: required ( "iban" )
505+ . with_semantic_type ( SemanticType :: Iban )
506+ . with_marking ( Marking :: Financial ) ;
507+ assert_eq ! ( SPEC . predicate, "iban" ) ;
508+ assert_eq ! ( SPEC . semantic_type, SemanticType :: Iban ) ;
509+ assert_eq ! ( SPEC . marking, Marking :: Financial ) ;
510+ }
511+
459512 #[ test]
460513 fn below_floor_required ( ) {
461514 let p = PropertySpec :: required ( "tax_id" ) ;
0 commit comments