operation_context.mojo (10813B)
1 # ADR-0025 D45 CB01-CB05 — corrected hyf_ops_v2 capability context. 2 # 3 # This module owns only the strict, capability-aware operation context for the 4 # three corrected operations selected by ``context.versions.schema == 5 # "hyf_ops_v2"``. It does not implement the operation pipelines (C008/C009), 6 # typed semantic consistency (C023) or activation (C042-C046). A recognized v2 7 # request is parsed here and then refused by the pre-activation guard in 8 # hyf_stdio.server; it must never reach the legacy shortcut handlers. 9 10 from std.collections import Dict, List, Optional 11 12 from json import Value 13 from json.deserialize import get_bool, get_int, get_string 14 15 16 comptime OPERATION_CONTRACT_SCHEMA_V2: String = "hyf_ops_v2" 17 comptime OPERATION_CONTEXT_DEFAULT_DEADLINE_MS: Int = 2500 18 19 20 def corrected_operation_selector() -> String: 21 return OPERATION_CONTRACT_SCHEMA_V2 22 23 24 def is_corrected_operation(capability: String) -> Bool: 25 return ( 26 capability == "farm_update.interpret" 27 or capability == "buyer_request.interpret" 28 or capability == "buyer_request.match" 29 ) 30 31 32 def corrected_operation_requires_farm_id(capability: String) -> Bool: 33 return capability == "farm_update.interpret" 34 35 36 def corrected_operation_activation_enabled() -> Bool: 37 """Activation boundary owned by C042-C046; C004 binds but never activates. 38 """ 39 return False 40 41 42 def _has_key(value: Value, key: String) -> Bool: 43 for candidate in value.object_keys(): 44 if candidate == key: 45 return True 46 return False 47 48 49 def _require_object(value: Value, context: String) raises: 50 if not value.is_object(): 51 raise Error(context + " must be a JSON object") 52 53 54 def _require_no_duplicate_keys(value: Value, context: String) raises: 55 # ADR-0026 D46 CR04: bounded linear seen-key admission. The previous 56 # all-pairs scan was quadratic in the decoded key count; this visits each 57 # entry once and rejects the first repeated decoded key. 58 var seen = Dict[String, Bool]() 59 for item in value.object_items(): 60 var key = String(item[0]) 61 if key in seen: 62 raise Error(context + " contains duplicate field '" + key + "'") 63 seen[key] = True 64 65 66 def _require_allowed_keys( 67 value: Value, allowed_keys: List[String], context: String 68 ) raises: 69 for key in value.object_keys(): 70 var allowed = False 71 for allowed_key in allowed_keys: 72 if key == allowed_key: 73 allowed = True 74 break 75 if not allowed: 76 raise Error(context + " contains unexpected field '" + key + "'") 77 78 79 def _has_nonblank(value: String) -> Bool: 80 return String(value).strip().byte_length() > 0 81 82 83 def _require_nonblank(value: String, context: String) raises: 84 if not _has_nonblank(value): 85 raise Error(context + " must not be blank") 86 87 88 def _optional_string( 89 value: Value, key: String, context: String 90 ) raises -> Optional[String]: 91 if not _has_key(value, key): 92 return None 93 var raw = get_string(value, key) 94 _require_nonblank(raw, context) 95 return String(raw) 96 97 98 def _required_string( 99 value: Value, key: String, context: String 100 ) raises -> String: 101 if not _has_key(value, key): 102 raise Error(context + " is required") 103 var raw = get_string(value, key) 104 _require_nonblank(raw, context) 105 return String(raw) 106 107 108 @fieldwise_init 109 struct OperationVersions(Copyable, Movable): 110 var schema: String 111 var taxonomy: String 112 var normalization: String 113 var review_policy: String 114 var ranking_policy: String 115 var question_bundle: String 116 var model: String 117 118 119 def _parse_operation_versions( 120 json: Value, context: String 121 ) raises -> OperationVersions: 122 _require_object(json, context) 123 _require_no_duplicate_keys(json, context) 124 125 var allowed_keys = List[String]() 126 for key in [ 127 "schema", 128 "taxonomy", 129 "normalization", 130 "review_policy", 131 "ranking_policy", 132 "question_bundle", 133 "model", 134 ]: 135 allowed_keys.append(key) 136 _require_allowed_keys(json, allowed_keys, context) 137 138 var schema = _required_string(json, "schema", context + " schema") 139 if schema != OPERATION_CONTRACT_SCHEMA_V2: 140 raise Error( 141 context 142 + " selects unsupported operation contract schema '" 143 + schema 144 + "'" 145 ) 146 147 return OperationVersions( 148 schema=schema, 149 taxonomy=_required_string(json, "taxonomy", context + " taxonomy"), 150 normalization=_required_string( 151 json, "normalization", context + " normalization" 152 ), 153 review_policy=_required_string( 154 json, "review_policy", context + " review_policy" 155 ), 156 ranking_policy=_required_string( 157 json, "ranking_policy", context + " ranking_policy" 158 ), 159 question_bundle=_required_string( 160 json, "question_bundle", context + " question_bundle" 161 ), 162 model=_required_string(json, "model", context + " model"), 163 ) 164 165 166 @fieldwise_init 167 struct OperationContext(Copyable, Movable): 168 var consumer: String 169 var execution_mode_preference: String 170 var deadline_ms: Int 171 var evaluation_time: String 172 var timezone: Optional[String] 173 var locale: Optional[String] 174 var versions: OperationVersions 175 var return_provenance: Bool 176 var actor_id: String 177 var farm_id: Optional[String] 178 179 180 def default_operation_context() -> OperationContext: 181 return OperationContext( 182 consumer="unknown", 183 execution_mode_preference="deterministic", 184 deadline_ms=OPERATION_CONTEXT_DEFAULT_DEADLINE_MS, 185 evaluation_time="", 186 timezone=None, 187 locale=None, 188 versions=OperationVersions( 189 schema="", 190 taxonomy="", 191 normalization="", 192 review_policy="", 193 ranking_policy="", 194 question_bundle="", 195 model="", 196 ), 197 return_provenance=False, 198 actor_id="", 199 farm_id=None, 200 ) 201 202 203 def operation_context_selects_v2(context_json: Value) raises -> Bool: 204 """True only for a well-typed ``context.versions.schema == hyf_ops_v2``. 205 206 Missing, null, wrong-type and unknown selectors are not recognized here; 207 they fall through to the unchanged legacy parser, which rejects 208 ``versions`` for unrelated capabilities and returns invalid_request. 209 210 ADR-0026 D46 CR04: this first-wins lookup is only used after the envelope 211 root duplicate gate has rejected any ambiguous v2-targeting envelope; use 212 ``context_selects_v2_any`` when duplicate-aware inspection is required. 213 """ 214 if not context_json.is_object(): 215 return False 216 if not _has_key(context_json, "versions"): 217 return False 218 var versions = context_json["versions"] 219 if not versions.is_object(): 220 return False 221 if not _has_key(versions, "schema"): 222 return False 223 var schema = versions["schema"] 224 if not schema.is_string(): 225 return False 226 return String(schema.string_value()) == OPERATION_CONTRACT_SCHEMA_V2 227 228 229 def context_selects_v2_any(context_json: Value) raises -> Bool: 230 """Duplicate-aware v2 selector inspection for the envelope admission gate. 231 232 Scans every decoded ``versions`` member and every decoded ``schema`` entry 233 instead of the first match, so a duplicate key cannot hide a v2 selector 234 behind an earlier legacy value. Not a substitute for unambiguous parsing: 235 the caller rejects the whole envelope when duplicate root keys are found. 236 """ 237 if not context_json.is_object(): 238 return False 239 for item in context_json.object_items(): 240 if item[0] != "versions": 241 continue 242 var versions = item[1].copy() 243 if not versions.is_object(): 244 continue 245 for member in versions.object_items(): 246 if member[0] == "schema" and member[1].is_string(): 247 if ( 248 String(member[1].string_value()) 249 == OPERATION_CONTRACT_SCHEMA_V2 250 ): 251 return True 252 return False 253 254 255 def parse_operation_context( 256 json: Value, capability: String 257 ) raises -> OperationContext: 258 if not is_corrected_operation(capability): 259 raise Error( 260 "operation context is only defined for corrected operations, not '" 261 + capability 262 + "'" 263 ) 264 265 _require_object(json, "operation context") 266 _require_no_duplicate_keys(json, "operation context") 267 268 var requires_farm = corrected_operation_requires_farm_id(capability) 269 var allowed_keys = List[String]() 270 for key in [ 271 "consumer", 272 "execution_mode_preference", 273 "deadline_ms", 274 "evaluation_time", 275 "timezone", 276 "locale", 277 "versions", 278 "return_provenance", 279 "actor_id", 280 ]: 281 allowed_keys.append(key) 282 if requires_farm: 283 allowed_keys.append("farm_id") 284 _require_allowed_keys(json, allowed_keys, "operation context") 285 286 var context = default_operation_context() 287 288 var consumer = _optional_string( 289 json, "consumer", "operation context consumer" 290 ) 291 if consumer: 292 context.consumer = consumer.value() 293 294 if _has_key(json, "execution_mode_preference"): 295 var preference = get_string(json, "execution_mode_preference") 296 if preference != "deterministic" and preference != "assisted": 297 raise Error( 298 "operation context execution_mode_preference must be" 299 " 'deterministic' or 'assisted'" 300 ) 301 context.execution_mode_preference = String(preference) 302 303 if _has_key(json, "deadline_ms"): 304 context.deadline_ms = get_int(json, "deadline_ms") 305 if context.deadline_ms <= 0: 306 raise Error( 307 "operation context deadline_ms must be greater than zero" 308 ) 309 310 context.evaluation_time = _required_string( 311 json, "evaluation_time", "operation context evaluation_time" 312 ) 313 314 context.timezone = _optional_string( 315 json, "timezone", "operation context timezone" 316 ) 317 context.locale = _optional_string( 318 json, "locale", "operation context locale" 319 ) 320 321 if not _has_key(json, "versions"): 322 raise Error("operation context versions is required") 323 context.versions = _parse_operation_versions( 324 json["versions"].clone(), "operation context versions" 325 ) 326 327 if _has_key(json, "return_provenance"): 328 context.return_provenance = get_bool(json, "return_provenance") 329 330 context.actor_id = _required_string( 331 json, "actor_id", "operation context actor_id" 332 ) 333 334 if requires_farm: 335 context.farm_id = _required_string( 336 json, "farm_id", "operation context farm_id" 337 ) 338 339 return context^