1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! §Fase 119.c — the name-keyed `ots` transformer registry.
//!
//! # Why this exists
//!
//! §111 F18: *"`apply_ots_to_target` is literally `target.to_string()`; no
//! ots_registry exists anywhere, so the documented 'enterprise override' has
//! no hook to override."* An `ots` whose application is the identity function
//! is the same defect as the mandate that transformed nothing: a primitive
//! that promises transformation, applies nothing, and can never fail.
//!
//! This registry is that hook, made real — the exact shape of
//! [`crate::shield_registry`] (§40.b), which is the proven pattern for
//! "OSS framework + registered implementation": a name-keyed table of
//! transformers, consulted at dispatch, with a verdict type that can REFUSE.
//!
//! # The one deliberate difference from `shield_registry`
//!
//! An UNREGISTERED shield passes content through unmodified — that is
//! shield's documented contract (no scanner ⇒ data unmodified), and it is
//! honest there because a shield is a *filter*: absence of filtering leaves
//! the data exactly as true as it was.
//!
//! An unregistered `ots` REFUSES (`run_ots_apply` fails the flow). An `ots`
//! is a *transformation*: its declaration promises that the output IS the
//! transformed input, and passing the input through unchanged under a
//! transformation's name fabricates a result — the §111 F18 lie, which this
//! fase exists to end. Absence of a transformer is not a weaker transform;
//! it is the inability to honor the declaration, and it is said out loud.
//!
//! # What registers here
//!
//! - The in-tree media pipeline (`crate::ots::TransformerRegistry`, §51-era)
//! remains the buffer-typed engine for audio/voice paths; a name-keyed
//! entry here may delegate into it.
//! - Enterprise verticals register their transformers exactly as they
//! register shield scanners.
//! - Tests register deterministic transformers to prove the dispatch path —
//! which is the point: the hook is real because a test can watch it fire,
//! and watch its absence refuse.
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
/// Context handed to a transformer alongside the content.
#[derive(Debug, Clone)]
pub struct OtsTransformContext {
/// The `ots` declaration's name (`ThreatPatcher`, …).
pub ots_name: String,
/// The declared teleology, verbatim — the intent the transform serves.
pub teleology: String,
/// The declared homotopy search mode (closed catalog:
/// deep | shallow | speculative).
pub homotopy_search: String,
/// The declared loss function, verbatim.
pub loss_function: String,
}
/// What a transformer decided.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OtsVerdict {
/// The transformation succeeded; `content` is the transformed output.
Transformed(String),
/// The transformer could not honor the declaration for this input.
/// Surfaces as a flow error naming the ots — never a silent passthrough.
Refused { code: String, reason: String },
}
/// A registered `ots` transformer.
pub trait OtsTransformer: Send + Sync {
/// Transform `content` under the declaration in `ctx`.
fn transform(&self, content: &str, ctx: &OtsTransformContext) -> OtsVerdict;
}
static REGISTRY: RwLock<Option<HashMap<String, Arc<dyn OtsTransformer>>>> = RwLock::new(None);
/// Register a transformer for an `ots` declaration name. Later registrations
/// for the same name replace earlier ones (the shield-registry discipline).
pub fn register_ots_transformer(ots_name: impl Into<String>, t: Arc<dyn OtsTransformer>) {
let mut guard = REGISTRY.write().expect("ots registry poisoned");
guard
.get_or_insert_with(HashMap::new)
.insert(ots_name.into(), t);
}
/// Look up the transformer registered for `ots_name`.
pub fn lookup_ots_transformer(ots_name: &str) -> Option<Arc<dyn OtsTransformer>> {
let guard = REGISTRY.read().expect("ots registry poisoned");
guard.as_ref().and_then(|m| m.get(ots_name).cloned())
}
/// Remove a registration (test hygiene).
pub fn unregister_ots_transformer(ots_name: &str) -> Option<Arc<dyn OtsTransformer>> {
let mut guard = REGISTRY.write().expect("ots registry poisoned");
guard.as_mut().and_then(|m| m.remove(ots_name))
}
#[cfg(test)]
mod tests {
use super::*;
struct Upper;
impl OtsTransformer for Upper {
fn transform(&self, content: &str, _ctx: &OtsTransformContext) -> OtsVerdict {
OtsVerdict::Transformed(content.to_uppercase())
}
}
#[test]
fn register_lookup_roundtrip_and_replacement() {
register_ots_transformer("ots-reg-test", Arc::new(Upper));
let t = lookup_ots_transformer("ots-reg-test").expect("registered");
let ctx = OtsTransformContext {
ots_name: "ots-reg-test".into(),
teleology: String::new(),
homotopy_search: String::new(),
loss_function: String::new(),
};
assert_eq!(t.transform("abc", &ctx), OtsVerdict::Transformed("ABC".into()));
unregister_ots_transformer("ots-reg-test");
assert!(lookup_ots_transformer("ots-reg-test").is_none());
}
#[test]
fn unregistered_name_is_none_never_a_default() {
assert!(lookup_ots_transformer("no-such-ots").is_none());
}
}