personal memory agent
0

Configure Feed

Select the types of activity you want to include in your feed.

Renew SPP attestation unattended

Add fenced SPP prerequisite renewal through journal brain and Cortex, including expected active-fingerprint/absence fences for fallback refreshes. Rotate stale process-local RA-TLS transport inline without forwarding bytes on failed replacement, and bound nvattest appraisal with redacted timeout handling.

+3017 -42
+130 -3
solstone/think/brain_cli.py
··· 34 34 BrainEvidenceComponent, 35 35 BrainProbeOutcome, 36 36 BrainStateConflictError, 37 + BrainStateExpectedFingerprintStaleError, 37 38 BrainStateInspection, 38 39 BrainStateRecord, 40 + abandon_brain_prerequisite_renewal, 39 41 abandon_brain_refresh, 42 + begin_brain_prerequisite_renewal, 40 43 begin_brain_refresh, 41 44 brain_state_path, 42 45 build_active_brain_fingerprint, 46 + finish_brain_prerequisite_renewal, 43 47 finish_brain_refresh, 44 48 inspect_brain_state, 45 49 probe_brain_refresh_lease_held, 50 + read_active_brain_fingerprint_sha256, 46 51 runtime_phase_reason, 47 52 ) 48 53 from solstone.think.providers.runtime_health import ( ··· 331 336 return _current_bundled_runtime_fingerprint(config) == expected 332 337 333 338 339 + def _expected_active_fingerprint_matches(expected: str) -> bool: 340 + try: 341 + return read_active_brain_fingerprint_sha256() == expected 342 + except Exception: 343 + return False 344 + 345 + 346 + def _expected_refresh_fingerprint_matches( 347 + args: argparse.Namespace, expected: str 348 + ) -> bool: 349 + if getattr(args, "expected_active_fingerprint", False): 350 + return _expected_active_fingerprint_matches(expected) 351 + return _expected_fingerprint_matches(expected) 352 + 353 + 334 354 def _runtime_diagnostic( 335 355 reason_code: str, 336 356 *, ··· 422 442 try: 423 443 recheck_confidential_attestation() 424 444 except AttestationStaleError: 445 + # Compatibility for verifier implementations that report stale directly. 425 446 return _failed_component(now, "attestation_expired"), "attestation_expired" 426 447 except AttestationFailedError: 427 448 return _failed_component(now, "attestation_rejected"), "attestation_rejected" ··· 620 641 return brain_exit_code(refresh_outcome="stale_expected_fingerprint") 621 642 622 643 644 + def _refresh_args_for_active_fallback(args: argparse.Namespace) -> argparse.Namespace: 645 + return argparse.Namespace( 646 + json=args.json, 647 + expected_fingerprint=args.expected_fingerprint, 648 + expected_active_fingerprint=True, 649 + expect_active_fingerprint_absent=False, 650 + ) 651 + 652 + 653 + def _run_renew_prerequisites(args: argparse.Namespace) -> int: 654 + now = _now() 655 + expected = args.expected_fingerprint 656 + if expected and not _expected_active_fingerprint_matches(expected): 657 + return _render_stale_expected(args) 658 + 659 + begin = begin_brain_prerequisite_renewal( 660 + now, 661 + expected_fingerprint_sha256=expected, 662 + run_id=uuid.uuid4().hex, 663 + ) 664 + if begin["status"] == "busy": 665 + inspection = inspect_brain_state(now) 666 + view = _view_from_inspection(inspection, now) 667 + busy = _transient_view( 668 + "busy", 669 + active_lane=view.active_lane, 670 + active_provider=view.active_provider, 671 + active_model=view.active_model, 672 + fingerprint_sha256=view.fingerprint_sha256, 673 + ) 674 + render(busy, json_output=args.json) 675 + return brain_exit_code(refresh_outcome="busy") 676 + if begin["status"] == "unsafe": 677 + return _run_refresh(_refresh_args_for_active_fallback(args)) 678 + 679 + permit = begin["permit"] 680 + try: 681 + lane_prerequisites, _reason = _spp_prerequisite(now) 682 + except Exception: 683 + LOG.exception("brain prerequisite renewal probe failed") 684 + try: 685 + record = abandon_brain_prerequisite_renewal( 686 + permit, 687 + "probe_internal_error", 688 + now, 689 + ) 690 + except BrainStateConflictError: 691 + lost = _transient_view("lost_fence") 692 + render(lost, json_output=args.json) 693 + return brain_exit_code(refresh_outcome="lost_fence") 694 + else: 695 + try: 696 + record = finish_brain_prerequisite_renewal( 697 + permit, 698 + lane_prerequisites, 699 + now, 700 + ) 701 + except BrainStateConflictError: 702 + lost = _transient_view("lost_fence") 703 + render(lost, json_output=args.json) 704 + return brain_exit_code(refresh_outcome="lost_fence") 705 + 706 + inspection = inspect_brain_state(now) 707 + view = _view_from_inspection(inspection, now) 708 + render(view, json_output=args.json) 709 + return brain_exit_code(aggregate_state=record["aggregate_state"]) 710 + 711 + 623 712 def _run_refresh(args: argparse.Namespace) -> int: 624 713 now = _now() 625 714 expected = args.expected_fingerprint 626 - if expected and not _expected_fingerprint_matches(expected): 715 + expected_active = getattr(args, "expected_active_fingerprint", False) 716 + expected_absent = getattr(args, "expect_active_fingerprint_absent", False) 717 + if expected and expected_absent: 718 + return _render_stale_expected(args) 719 + if expected and not _expected_refresh_fingerprint_matches(args, expected): 627 720 return _render_stale_expected(args) 628 721 629 722 inspection = inspect_brain_state(now) ··· 631 724 if view.reason_code == "configuration_invalid": 632 725 render(view, json_output=args.json) 633 726 return brain_exit_code(aggregate_state=view.aggregate_state) 634 - if expected and view.aggregate_state == "ready": 727 + if ( 728 + expected 729 + and not expected_active 730 + and not expected_absent 731 + and view.aggregate_state == "ready" 732 + ): 635 733 render(view, json_output=args.json) 636 734 return brain_exit_code(aggregate_state=view.aggregate_state) 637 735 638 - permit = begin_brain_refresh(now, run_id=uuid.uuid4().hex) 736 + try: 737 + permit = begin_brain_refresh( 738 + now, 739 + run_id=uuid.uuid4().hex, 740 + expected_active_fingerprint_sha256=expected if expected_active else None, 741 + expect_active_fingerprint_absent=expected_absent, 742 + ) 743 + except BrainStateExpectedFingerprintStaleError: 744 + return _render_stale_expected(args) 639 745 if permit is None: 640 746 busy = False 641 747 try: ··· 719 825 "--expected-fingerprint", 720 826 help="Only refresh if the bundled runtime fingerprint still matches", 721 827 ) 828 + refresh_parser.add_argument( 829 + "--expected-active-fingerprint", 830 + action="store_true", 831 + help="Interpret --expected-fingerprint as the active brain fingerprint", 832 + ) 833 + refresh_parser.add_argument( 834 + "--expect-active-fingerprint-absent", 835 + action="store_true", 836 + help="Only refresh if no active brain fingerprint exists yet", 837 + ) 838 + renew_parser = subparsers.add_parser( 839 + "renew-prerequisites", 840 + help=argparse.SUPPRESS, 841 + ) 842 + renew_parser.add_argument("--json", action="store_true", help=argparse.SUPPRESS) 843 + renew_parser.add_argument( 844 + "--expected-fingerprint", 845 + help=argparse.SUPPRESS, 846 + ) 722 847 return parser 723 848 724 849 ··· 727 852 return _run_status(args) 728 853 if args.subcommand == "refresh": 729 854 return _run_refresh(args) 855 + if args.subcommand == "renew-prerequisites": 856 + return _run_renew_prerequisites(args) 730 857 parser.print_help() 731 858 return 2 732 859
+575 -3
solstone/think/cortex.py
··· 26 26 import sys 27 27 import threading 28 28 import time 29 + import uuid 30 + from collections.abc import Callable 29 31 from datetime import datetime, timezone 30 32 from pathlib import Path 31 33 from typing import Any, Dict, Optional 32 34 33 35 from solstone.think.callosum import CallosumConnection 34 36 from solstone.think.models import calc_agent_cost 35 - from solstone.think.providers.brain_state import inspect_brain_state 37 + from solstone.think.providers.brain_state import ( 38 + inspect_brain_state, 39 + read_active_brain_fingerprint_sha256, 40 + ) 36 41 from solstone.think.runner import _atomic_symlink 42 + from solstone.think.services.spp_attest.cadence import TPM_HEARTBEAT_INTERVAL 37 43 from solstone.think.talent import get_output_path 38 44 from solstone.think.talents import TALENT_EXECUTION_MODULE 39 45 from solstone.think.utils import get_journal, get_rev, now_ms ··· 100 106 return 101 107 102 108 109 + SPP_RENEWAL_ATTEMPT_BOUND_S = 120.0 110 + SPP_REFRESH_OBSERVATION_BOUND_S = 300.0 111 + SPP_RENEWAL_RETRY_DELAYS_S = (5.0, 10.0, 20.0, 40.0, 60.0) 112 + SPP_RENEWAL_PROACTIVE_MARGIN_S = ( 113 + SPP_RENEWAL_ATTEMPT_BOUND_S + SPP_RENEWAL_RETRY_DELAYS_S[0] 114 + ) 115 + SPP_RENEWAL_ACK_TIMEOUT_S = 15.0 116 + SPP_RENEWAL_MAX_WAIT_S = 60.0 117 + assert SPP_RENEWAL_PROACTIVE_MARGIN_S < TPM_HEARTBEAT_INTERVAL.total_seconds() / 2 118 + 119 + 120 + class SppRenewalController: 121 + """Unattended SPP prerequisite renewal controller for Cortex.""" 122 + 123 + def __init__( 124 + self, 125 + *, 126 + callosum: CallosumConnection, 127 + stop_event: threading.Event, 128 + logger: logging.Logger, 129 + clock: Callable[[], datetime], 130 + wait: Callable[[float], bool], 131 + journal_path: Path, 132 + ) -> None: 133 + self.callosum = callosum 134 + self.stop_event = stop_event 135 + self.logger = logger 136 + self.clock = clock 137 + self.wait = wait 138 + self.journal_path = journal_path 139 + self._pending_ref: str | None = None 140 + self._pending_action: str | None = None 141 + self._pending_fingerprint: str | None = None 142 + self._pending_expect_fingerprint_absent = False 143 + self._pending_observed_at: datetime | None = None 144 + self._pending_expires_at: datetime | None = None 145 + self._ack_deadline: datetime | None = None 146 + self._running_ref: str | None = None 147 + self._running_action: str | None = None 148 + self._running_fingerprint: str | None = None 149 + self._running_expect_fingerprint_absent = False 150 + self._running_observed_at: datetime | None = None 151 + self._running_expires_at: datetime | None = None 152 + self._running_deadline: datetime | None = None 153 + self._successor_after_ref: str | None = None 154 + self._successor_deadline: datetime | None = None 155 + self._retry_index = 0 156 + self._retry_after: datetime | None = None 157 + self._last_mode: str | None = None 158 + 159 + def run(self) -> None: 160 + while not self.stop_event.is_set(): 161 + try: 162 + delay = self.step() 163 + except Exception as exc: 164 + now = self._now() 165 + self._handle_step_exception(exc, now) 166 + delay = self._seconds_until( 167 + self._retry_after, now, default=SPP_RENEWAL_RETRY_DELAYS_S[0] 168 + ) 169 + self.wait(max(0.0, min(delay, SPP_RENEWAL_MAX_WAIT_S))) 170 + 171 + def step(self) -> float: 172 + now = self._now() 173 + try: 174 + return self._step(now) 175 + except Exception as exc: 176 + self._handle_step_exception(exc, now) 177 + return self._seconds_until( 178 + self._retry_after, now, default=SPP_RENEWAL_RETRY_DELAYS_S[0] 179 + ) 180 + 181 + def _step(self, now: datetime) -> float: 182 + if self._spp_disabled(now): 183 + self._clear_demand() 184 + if self._last_mode != "disabled": 185 + self._log("disabled", reason="non_spp_lane") 186 + self._last_mode = "disabled" 187 + return 30.0 188 + if self._pending_ref is not None: 189 + if self._ack_deadline is not None and now >= self._ack_deadline: 190 + self._log("failed", reason="start_ack_timeout", ref=self._pending_ref) 191 + self._clear_pending() 192 + self._schedule_retry(now) 193 + return self._seconds_until(self._ack_deadline, now, default=5.0) 194 + if self._running_ref is not None: 195 + if self._running_deadline is not None and now >= self._running_deadline: 196 + self._log( 197 + "stale", reason="running_observation_timeout", ref=self._running_ref 198 + ) 199 + self._clear_running() 200 + self._schedule_retry(now) 201 + return self._seconds_until( 202 + self._retry_after, now, default=SPP_RENEWAL_RETRY_DELAYS_S[0] 203 + ) 204 + return self._seconds_until(self._running_deadline, now, default=5.0) 205 + if self._successor_after_ref is not None: 206 + if self._successor_deadline is not None and now < self._successor_deadline: 207 + return self._seconds_until( 208 + self._successor_deadline, 209 + now, 210 + default=5.0, 211 + ) 212 + self._log( 213 + "stale", 214 + reason="successor_observation_timeout", 215 + active_ref=self._successor_after_ref, 216 + ) 217 + self._clear_successor() 218 + if self._retry_after is not None: 219 + if now < self._retry_after: 220 + return self._seconds_until(self._retry_after, now, default=5.0) 221 + self._retry_after = None 222 + 223 + plan = self._plan(now) 224 + if plan["action"] == "disabled": 225 + self._clear_demand() 226 + if self._last_mode != "disabled": 227 + self._log("disabled", reason=plan.get("reason")) 228 + self._last_mode = "disabled" 229 + return 30.0 230 + self._last_mode = plan["action"] 231 + if plan["action"] == "checking": 232 + return 5.0 233 + if plan["action"] == "wait": 234 + return float(plan["delay"]) 235 + if plan["action"] in {"renew", "refresh"}: 236 + if self._send_request(plan): 237 + return SPP_RENEWAL_ACK_TIMEOUT_S 238 + return self._seconds_until( 239 + self._retry_after, 240 + now, 241 + default=SPP_RENEWAL_RETRY_DELAYS_S[0], 242 + ) 243 + return 30.0 244 + 245 + def _spp_disabled(self, now: datetime) -> bool: 246 + inspection = inspect_brain_state(now, journal_path=self.journal_path) 247 + return inspection["projection"]["active_lane"] != "spp" 248 + 249 + def handle_supervisor_message(self, message: dict[str, Any]) -> None: 250 + if message.get("tract") != "supervisor": 251 + return 252 + event = message.get("event") 253 + ref = message.get("ref") 254 + now = self._now() 255 + if event == "started" and ref == self._pending_ref: 256 + self._running_ref = self._pending_ref 257 + self._running_action = self._pending_action 258 + self._running_fingerprint = self._pending_fingerprint 259 + self._running_expect_fingerprint_absent = ( 260 + self._pending_expect_fingerprint_absent 261 + ) 262 + self._running_observed_at = self._pending_observed_at 263 + self._running_expires_at = self._pending_expires_at 264 + self._running_deadline = datetime.fromtimestamp( 265 + now.timestamp() + self._observation_bound(self._pending_action), 266 + tz=timezone.utc, 267 + ) 268 + self._log("in_flight", ref=self._running_ref, action=self._running_action) 269 + self._clear_pending(keep_retry=True) 270 + return 271 + if event == "skipped" and ref == self._pending_ref: 272 + active_ref = message.get("active_ref") 273 + self._log( 274 + "in_flight", 275 + reason=str(message.get("reason") or "skipped"), 276 + ref=ref, 277 + active_ref=str(active_ref) if active_ref else None, 278 + ) 279 + self._successor_after_ref = str(active_ref) if active_ref else None 280 + self._successor_deadline = ( 281 + datetime.fromtimestamp( 282 + now.timestamp() + SPP_REFRESH_OBSERVATION_BOUND_S, 283 + tz=timezone.utc, 284 + ) 285 + if self._successor_after_ref is not None 286 + else None 287 + ) 288 + self._clear_pending(keep_retry=True) 289 + if self._successor_after_ref is None: 290 + self._schedule_retry(now) 291 + return 292 + if event == "stopped": 293 + if ref == self._running_ref: 294 + self._verify_running_result(self._exit_code(message), now) 295 + return 296 + if ref == self._successor_after_ref: 297 + self._clear_successor() 298 + 299 + def _plan(self, now: datetime) -> dict[str, Any]: 300 + inspection = inspect_brain_state(now, journal_path=self.journal_path) 301 + projection = inspection["projection"] 302 + if projection["active_lane"] != "spp": 303 + return {"action": "disabled", "reason": "non_spp_lane"} 304 + if projection["aggregate_state"] == "checking": 305 + return {"action": "checking"} 306 + 307 + fingerprint = read_active_brain_fingerprint_sha256( 308 + journal_path=self.journal_path 309 + ) 310 + if fingerprint is None: 311 + return { 312 + "action": "refresh", 313 + "fingerprint": None, 314 + "expect_fingerprint_absent": True, 315 + } 316 + record = inspection["record"] 317 + component = None 318 + if record is not None: 319 + component = record["evidence"].get("lane_prerequisites") 320 + observed_at = None 321 + expires_at = None 322 + if isinstance(component, dict): 323 + observed_at = self._parse_time(component.get("observed_at")) 324 + expires_at = self._parse_time(component.get("expires_at")) 325 + if ( 326 + projection["aggregate_state"] != "ready" 327 + or not isinstance(record, dict) 328 + or record.get("fingerprint_sha256") != fingerprint 329 + or not isinstance(component, dict) 330 + or component.get("status") != "ok" 331 + ): 332 + return { 333 + "action": "refresh", 334 + "fingerprint": fingerprint, 335 + "observed_at": observed_at, 336 + "expires_at": expires_at, 337 + } 338 + 339 + if observed_at is None or expires_at is None: 340 + return { 341 + "action": "refresh", 342 + "fingerprint": fingerprint, 343 + "observed_at": observed_at, 344 + "expires_at": expires_at, 345 + } 346 + if now >= expires_at: 347 + return { 348 + "action": "refresh", 349 + "fingerprint": fingerprint, 350 + "observed_at": observed_at, 351 + "expires_at": expires_at, 352 + } 353 + renew_at = expires_at.timestamp() - SPP_RENEWAL_PROACTIVE_MARGIN_S 354 + delay = renew_at - now.timestamp() 355 + if delay > 0: 356 + self._log("scheduled", delay_s=round(delay, 3)) 357 + return {"action": "wait", "delay": delay} 358 + return { 359 + "action": "renew", 360 + "fingerprint": fingerprint, 361 + "observed_at": observed_at, 362 + "expires_at": expires_at, 363 + } 364 + 365 + def _send_request(self, plan: dict[str, Any]) -> bool: 366 + action = str(plan["action"]) 367 + fingerprint = plan.get("fingerprint") 368 + ref = f"spp-renewal-{uuid.uuid4().hex}" 369 + if action == "renew": 370 + cmd = [ 371 + "journal", 372 + "brain", 373 + "renew-prerequisites", 374 + "--json", 375 + "--expected-fingerprint", 376 + str(fingerprint), 377 + ] 378 + else: 379 + expect_absent = bool(plan.get("expect_fingerprint_absent")) 380 + if fingerprint is None and not expect_absent: 381 + self._log("failed", reason="fingerprint_unavailable", action=action) 382 + self._schedule_retry(self._now()) 383 + return False 384 + cmd = ["journal", "brain", "refresh", "--json"] 385 + if expect_absent: 386 + cmd.append("--expect-active-fingerprint-absent") 387 + else: 388 + cmd.extend( 389 + [ 390 + "--expected-fingerprint", 391 + str(fingerprint), 392 + "--expected-active-fingerprint", 393 + ] 394 + ) 395 + now = self._now() 396 + try: 397 + self.callosum.emit( 398 + "supervisor", 399 + "request", 400 + cmd=cmd, 401 + ref=ref, 402 + scheduler_name="spp-renewal", 403 + ) 404 + except Exception as exc: 405 + self._log("failed", reason=type(exc).__name__, action=action) 406 + self._schedule_retry(now) 407 + return False 408 + self._pending_ref = ref 409 + self._pending_action = action 410 + self._pending_fingerprint = str(fingerprint) if fingerprint else None 411 + self._pending_expect_fingerprint_absent = bool( 412 + plan.get("expect_fingerprint_absent") 413 + ) 414 + self._pending_observed_at = plan.get("observed_at") 415 + self._pending_expires_at = plan.get("expires_at") 416 + self._ack_deadline = datetime.fromtimestamp( 417 + now.timestamp() + SPP_RENEWAL_ACK_TIMEOUT_S, tz=timezone.utc 418 + ) 419 + self._log("in_flight", ref=ref, action=action) 420 + return True 421 + 422 + def _verify_running_result(self, exit_code: int, now: datetime) -> None: 423 + action = self._running_action 424 + fingerprint = self._running_fingerprint 425 + expect_absent = self._running_expect_fingerprint_absent 426 + previous_observed = self._running_observed_at 427 + previous_expires = self._running_expires_at 428 + ref = self._running_ref 429 + self._clear_running() 430 + if action == "renew" and self._persisted_spp_prerequisite_verified( 431 + fingerprint, 432 + previous_observed, 433 + previous_expires, 434 + now, 435 + require_ready=False, 436 + ): 437 + self._retry_index = 0 438 + self._retry_after = None 439 + self._log("verified", ref=ref) 440 + return 441 + if ( 442 + action == "refresh" 443 + and exit_code == 0 444 + and ( 445 + self._persisted_spp_prerequisite_verified( 446 + fingerprint, 447 + previous_observed, 448 + previous_expires, 449 + now, 450 + require_ready=True, 451 + ) 452 + if not expect_absent 453 + else self._persisted_spp_absence_bootstrap_verified( 454 + previous_observed, 455 + previous_expires, 456 + now, 457 + ) 458 + ) 459 + ): 460 + self._retry_index = 0 461 + self._retry_after = None 462 + self._log("verified", ref=ref, action="refresh") 463 + return 464 + self._log("failed", ref=ref, action=action, exit_code=exit_code) 465 + self._schedule_retry(now) 466 + 467 + def _persisted_spp_prerequisite_verified( 468 + self, 469 + fingerprint: str | None, 470 + previous_observed: datetime | None, 471 + previous_expires: datetime | None, 472 + now: datetime, 473 + *, 474 + require_ready: bool, 475 + ) -> bool: 476 + if fingerprint is None: 477 + return False 478 + try: 479 + active_fingerprint = read_active_brain_fingerprint_sha256( 480 + journal_path=self.journal_path 481 + ) 482 + inspection = inspect_brain_state(now, journal_path=self.journal_path) 483 + except Exception: 484 + return False 485 + if active_fingerprint != fingerprint: 486 + return False 487 + projection = inspection["projection"] 488 + if projection["active_lane"] != "spp": 489 + return False 490 + if require_ready and projection["aggregate_state"] != "ready": 491 + return False 492 + record = inspection["record"] 493 + if record is None or record["active_lane"] != "spp": 494 + return False 495 + if record["fingerprint_sha256"] != fingerprint: 496 + return False 497 + component = record["evidence"].get("lane_prerequisites") 498 + if not isinstance(component, dict) or component.get("status") != "ok": 499 + return False 500 + observed_at = self._parse_time(component.get("observed_at")) 501 + expires_at = self._parse_time(component.get("expires_at")) 502 + return ( 503 + observed_at is not None 504 + and expires_at is not None 505 + and (previous_observed is None or observed_at > previous_observed) 506 + and (previous_expires is None or expires_at > previous_expires) 507 + ) 508 + 509 + def _persisted_spp_absence_bootstrap_verified( 510 + self, 511 + previous_observed: datetime | None, 512 + previous_expires: datetime | None, 513 + now: datetime, 514 + ) -> bool: 515 + try: 516 + active_fingerprint = read_active_brain_fingerprint_sha256( 517 + journal_path=self.journal_path 518 + ) 519 + inspection = inspect_brain_state(now, journal_path=self.journal_path) 520 + except Exception: 521 + return False 522 + if active_fingerprint is None: 523 + return False 524 + projection = inspection["projection"] 525 + if ( 526 + projection["active_lane"] != "spp" 527 + or projection["aggregate_state"] != "ready" 528 + ): 529 + return False 530 + record = inspection["record"] 531 + if record is None or record["active_lane"] != "spp": 532 + return False 533 + if record["fingerprint_sha256"] != active_fingerprint: 534 + return False 535 + component = record["evidence"].get("lane_prerequisites") 536 + if not isinstance(component, dict) or component.get("status") != "ok": 537 + return False 538 + observed_at = self._parse_time(component.get("observed_at")) 539 + expires_at = self._parse_time(component.get("expires_at")) 540 + return ( 541 + observed_at is not None 542 + and expires_at is not None 543 + and (previous_observed is None or observed_at > previous_observed) 544 + and (previous_expires is None or expires_at > previous_expires) 545 + ) 546 + 547 + def _schedule_retry(self, now: datetime) -> None: 548 + delay = SPP_RENEWAL_RETRY_DELAYS_S[ 549 + min(self._retry_index, len(SPP_RENEWAL_RETRY_DELAYS_S) - 1) 550 + ] 551 + self._retry_index += 1 552 + self._retry_after = datetime.fromtimestamp( 553 + now.timestamp() + delay, tz=timezone.utc 554 + ) 555 + self._log("retrying", delay_s=delay) 556 + 557 + def _handle_step_exception(self, exc: Exception, now: datetime) -> None: 558 + self._log("failed", reason=type(exc).__name__) 559 + self._clear_pending(keep_retry=True) 560 + self._clear_running() 561 + self._clear_successor() 562 + self._schedule_retry(now) 563 + 564 + def _observation_bound(self, action: str | None) -> float: 565 + if action == "refresh": 566 + return SPP_REFRESH_OBSERVATION_BOUND_S 567 + return SPP_RENEWAL_ATTEMPT_BOUND_S 568 + 569 + def _exit_code(self, message: dict[str, Any]) -> int: 570 + value = message.get("exit_code") 571 + if value is None: 572 + return -1 573 + try: 574 + return int(value) 575 + except (TypeError, ValueError): 576 + return -1 577 + 578 + def _clear_pending(self, *, keep_retry: bool = False) -> None: 579 + self._pending_ref = None 580 + self._pending_action = None 581 + self._pending_fingerprint = None 582 + self._pending_expect_fingerprint_absent = False 583 + self._pending_observed_at = None 584 + self._pending_expires_at = None 585 + self._ack_deadline = None 586 + if not keep_retry: 587 + self._retry_after = None 588 + 589 + def _clear_running(self) -> None: 590 + self._running_ref = None 591 + self._running_action = None 592 + self._running_fingerprint = None 593 + self._running_expect_fingerprint_absent = False 594 + self._running_observed_at = None 595 + self._running_expires_at = None 596 + self._running_deadline = None 597 + 598 + def _clear_successor(self) -> None: 599 + self._successor_after_ref = None 600 + self._successor_deadline = None 601 + 602 + def _clear_demand(self) -> None: 603 + self._clear_pending() 604 + self._clear_running() 605 + self._clear_successor() 606 + self._retry_index = 0 607 + self._retry_after = None 608 + 609 + def _now(self) -> datetime: 610 + return self.clock().astimezone(timezone.utc) 611 + 612 + def _seconds_until( 613 + self, deadline: datetime | None, now: datetime, *, default: float 614 + ) -> float: 615 + if deadline is None: 616 + return default 617 + return max(0.0, deadline.timestamp() - now.timestamp()) 618 + 619 + def _parse_time(self, value: object) -> datetime | None: 620 + if not isinstance(value, str): 621 + return None 622 + try: 623 + return datetime.fromisoformat(value.replace("Z", "+00:00")).astimezone( 624 + timezone.utc 625 + ) 626 + except ValueError: 627 + return None 628 + 629 + def _log(self, event: str, **fields: object) -> None: 630 + safe = " ".join( 631 + f"{key}={value}" 632 + for key, value in sorted(fields.items()) 633 + if value is not None 634 + ) 635 + suffix = f" {safe}" if safe else "" 636 + self.logger.info("event=spp_renewal_%s%s", event, suffix) 637 + 638 + 103 639 class CortexService: 104 640 """Callosum-based talent process manager.""" 105 641 106 - def __init__(self, journal_path: Optional[str] = None): 642 + def __init__( 643 + self, 644 + journal_path: Optional[str] = None, 645 + *, 646 + clock: Callable[[], datetime] | None = None, 647 + wait: Callable[[float], bool] | None = None, 648 + ): 107 649 self.journal_path = Path(journal_path or get_journal()) 108 650 self.talents_dir = self.journal_path / "talents" 109 651 self.talents_dir.mkdir(parents=True, exist_ok=True) ··· 117 659 self.spawn_queue: queue.Queue = queue.Queue() 118 660 self._pending_spawns: int = 0 119 661 self._spawn_worker: threading.Thread | None = None 662 + self._clock = clock or (lambda: datetime.now(timezone.utc)) 663 + self._wait = wait or self.stop_event.wait 664 + self._spp_renewal_controller: SppRenewalController | None = None 665 + self._spp_renewal_worker: threading.Thread | None = None 120 666 121 667 # Callosum connection for receiving requests and broadcasting events 122 668 self.callosum = CallosumConnection(defaults={"rev": get_rev()}) ··· 264 810 daemon=True, 265 811 ) 266 812 self._spawn_worker.start() 813 + self._start_spp_renewal_controller() 267 814 268 815 self.logger.info("Cortex service started, listening for talent requests") 269 816 ··· 285 832 286 833 def _should_request_brain_refresh(self) -> bool: 287 834 try: 288 - inspection = inspect_brain_state(datetime.now(timezone.utc)) 835 + inspection = inspect_brain_state( 836 + self._clock(), journal_path=self.journal_path 837 + ) 289 838 except Exception: 290 839 return True 291 840 projection = inspection["projection"] 841 + if projection["active_lane"] == "spp": 842 + return False 292 843 if projection["aggregate_state"] in {"checking", "ready"}: 293 844 return False 294 845 return not projection["runtime_transition_in_progress"] 295 846 847 + def _start_spp_renewal_controller(self) -> None: 848 + self._spp_renewal_controller = SppRenewalController( 849 + callosum=self.callosum, 850 + stop_event=self.stop_event, 851 + logger=self.logger, 852 + clock=self._clock, 853 + wait=self._wait, 854 + journal_path=self.journal_path, 855 + ) 856 + self._spp_renewal_worker = threading.Thread( 857 + target=self._spp_renewal_controller.run, 858 + name="cortex-spp-renewal", 859 + daemon=True, 860 + ) 861 + self._spp_renewal_worker.start() 862 + 296 863 def _handle_callosum_message(self, message: Dict[str, Any]) -> None: 297 864 """Handle incoming Callosum messages (callback).""" 865 + if self._spp_renewal_controller is not None: 866 + self._spp_renewal_controller.handle_supervisor_message(message) 298 867 # Filter for cortex tract and request event 299 868 if message.get("tract") != "cortex" or message.get("event") != "request": 300 869 return ··· 906 1475 907 1476 if self.callosum: 908 1477 self.callosum.stop() 1478 + 1479 + if self._spp_renewal_worker is not None: 1480 + self._spp_renewal_worker.join(timeout=2.0) 909 1481 910 1482 # Let the spawn worker finish its current item and exit (~2s bound; the 911 1483 # worker's 0.5s get-timeout guarantees it observes stop_event promptly).
+3 -2
solstone/think/models.py
··· 292 292 293 293 294 294 # Attestation failures are non-retryable. AttestationFailedError is raised by 295 - # solstone.think.services.spp_attest.composite.verify_composite; AttestationStaleError 296 - # is reserved for the follow-on verifier when a session's cadence windows lapse. 295 + # solstone.think.services.spp_attest.composite.verify_composite. AttestationStaleError 296 + # remains part of the verifier contract; the process-local egress transport rotates 297 + # stale live sessions inline before forwarding bytes. 297 298 _CONFIDENTIAL_ATTESTATION_VERIFIER: Callable[[dict[str, Any]], None] | None = None 298 299 299 300
+353 -11
solstone/think/providers/brain_state.py
··· 523 523 error: str | None 524 524 525 525 526 + BrainPrerequisiteRenewalStatus = Literal["started", "busy", "unsafe"] 527 + 528 + 529 + class BrainPrerequisiteRenewalBeginResult(TypedDict): 530 + status: BrainPrerequisiteRenewalStatus 531 + permit: NotRequired["BrainRefreshPermit"] 532 + reason: NotRequired[str] 533 + 534 + 526 535 class BrainStateValidationError(ValueError): 527 536 """Raised when a persisted brain state record violates the closed schema.""" 528 537 ··· 534 543 535 544 class BrainStateConflictError(RuntimeError): 536 545 """Raised when a stale refresh permit attempts to finalize.""" 546 + 547 + 548 + class BrainStateExpectedFingerprintStaleError(BrainStateConflictError): 549 + """Raised when a fenced refresh observes a different active fingerprint state.""" 537 550 538 551 539 552 @dataclass ··· 1885 1898 now: datetime, 1886 1899 *, 1887 1900 run_id: str | None = None, 1901 + expected_active_fingerprint_sha256: str | None = None, 1902 + expect_active_fingerprint_absent: bool = False, 1888 1903 journal_path: str | Path | None = None, 1889 1904 ) -> BrainRefreshPermit | None: 1890 1905 now = _utc(now) 1906 + if expected_active_fingerprint_sha256 is not None: 1907 + try: 1908 + _validate_hex( 1909 + expected_active_fingerprint_sha256, 1910 + "expected_active_fingerprint_sha256", 1911 + ) 1912 + except BrainStateValidationError as exc: 1913 + raise BrainStateExpectedFingerprintStaleError(str(exc)) from exc 1914 + if ( 1915 + expected_active_fingerprint_sha256 is not None 1916 + and expect_active_fingerprint_absent 1917 + ): 1918 + raise ValueError( 1919 + "expected active fingerprint and expected absence are mutually exclusive" 1920 + ) 1921 + expected_contract = ( 1922 + expected_active_fingerprint_sha256 is not None 1923 + or expect_active_fingerprint_absent 1924 + ) 1891 1925 try: 1892 1926 config = read_journal_config(journal_path) 1893 1927 except (CorruptConfigError, OSError): 1894 1928 return None 1895 1929 lane, provider, model = _derive_lane(config) 1896 1930 path = brain_state_path(journal_path=journal_path) 1897 - if lane is None: 1931 + if lane is None and not expected_contract: 1898 1932 return None 1899 - if lane == "none": 1933 + if lane == "none" and not expected_contract: 1900 1934 _begin_nonrefresh_record( 1901 1935 now, 1902 1936 path=path, ··· 1908 1942 if lease is None: 1909 1943 return None 1910 1944 try: 1911 - try: 1912 - key = _load_or_generate_fingerprint_key(journal_path=journal_path) 1913 - except Exception: 1914 - lease.release() 1915 - return None 1916 - fingerprint = build_active_brain_fingerprint(config, hmac_key=key) 1917 - if fingerprint["active_lane"] is None or not fingerprint["ok"]: 1918 - lease.release() 1919 - return None 1920 1945 run_id = run_id or uuid.uuid4().hex 1921 1946 expires_at = now + CHECKING_TTL 1922 1947 with hold_lock(path, mode=BRAIN_FILE_MODE): 1948 + try: 1949 + config = read_journal_config(journal_path) 1950 + except (CorruptConfigError, OSError): 1951 + lease.release() 1952 + return None 1953 + lane, provider, model = _derive_lane(config) 1954 + if lane is None: 1955 + if expected_contract: 1956 + raise BrainStateExpectedFingerprintStaleError( 1957 + "active brain fingerprint is unavailable" 1958 + ) 1959 + lease.release() 1960 + return None 1961 + if lane == "none": 1962 + if expected_contract: 1963 + raise BrainStateExpectedFingerprintStaleError( 1964 + "active brain fingerprint is unavailable" 1965 + ) 1966 + _begin_nonrefresh_record( 1967 + now, 1968 + path=path, 1969 + active_provider=provider, 1970 + active_model=model, 1971 + ) 1972 + lease.release() 1973 + return None 1974 + try: 1975 + key = _load_existing_fingerprint_key(journal_path=journal_path) 1976 + if expect_active_fingerprint_absent: 1977 + if key is not None: 1978 + raise BrainStateExpectedFingerprintStaleError( 1979 + "active brain fingerprint is present" 1980 + ) 1981 + key = _load_or_generate_fingerprint_key(journal_path=journal_path) 1982 + elif expected_active_fingerprint_sha256 is not None: 1983 + if key is None: 1984 + raise BrainStateExpectedFingerprintStaleError( 1985 + "active brain fingerprint is absent" 1986 + ) 1987 + else: 1988 + key = _load_or_generate_fingerprint_key(journal_path=journal_path) 1989 + except BrainStateExpectedFingerprintStaleError: 1990 + raise 1991 + except Exception: 1992 + lease.release() 1993 + return None 1994 + assert key is not None 1995 + fingerprint = build_active_brain_fingerprint(config, hmac_key=key) 1996 + if ( 1997 + expected_active_fingerprint_sha256 is not None 1998 + and fingerprint["fingerprint_sha256"] 1999 + != expected_active_fingerprint_sha256 2000 + ): 2001 + raise BrainStateExpectedFingerprintStaleError( 2002 + "active brain fingerprint changed" 2003 + ) 2004 + if fingerprint["active_lane"] is None or not fingerprint["ok"]: 2005 + if expected_contract: 2006 + raise BrainStateExpectedFingerprintStaleError( 2007 + "active brain fingerprint is unavailable" 2008 + ) 2009 + lease.release() 2010 + return None 1923 2011 current = _read_record_unlocked(path) 1924 2012 revision = _next_revision(current) 1925 2013 marker_seen = _runtime_failure_marker_id(current) ··· 1999 2087 return fingerprint["fingerprint_sha256"] 2000 2088 2001 2089 2090 + def _component_ok_unexpired( 2091 + component: BrainEvidenceComponent | None, 2092 + component_name: str, 2093 + now: datetime, 2094 + ) -> bool: 2095 + if component is None or component["status"] != "ok": 2096 + return False 2097 + try: 2098 + expires_at = _parse_timestamp( 2099 + component.get("expires_at"), f"evidence.{component_name}.expires_at" 2100 + ) 2101 + except BrainStateValidationError: 2102 + return False 2103 + return now < expires_at 2104 + 2105 + 2106 + def _safe_prerequisite_renewal_evidence( 2107 + current: BrainStateRecord, 2108 + now: datetime, 2109 + ) -> BrainEvidenceRecord | None: 2110 + if current["active_lane"] != "spp": 2111 + return None 2112 + if _record_timestamp_invalid(current, now): 2113 + return None 2114 + evidence = current["evidence"] 2115 + preserved: dict[str, BrainEvidenceComponent | None] = dict(evidence) 2116 + for component_name in ("configuration", "generate", "cogitate"): 2117 + if not _component_ok_unexpired(preserved[component_name], component_name, now): 2118 + return None 2119 + return cast(BrainEvidenceRecord, preserved) 2120 + 2121 + 2122 + def _prerequisite_renewal_begin_result( 2123 + status: BrainPrerequisiteRenewalStatus, 2124 + *, 2125 + permit: BrainRefreshPermit | None = None, 2126 + reason: str | None = None, 2127 + ) -> BrainPrerequisiteRenewalBeginResult: 2128 + result: BrainPrerequisiteRenewalBeginResult = {"status": status} 2129 + if permit is not None: 2130 + result["permit"] = permit 2131 + if reason is not None: 2132 + result["reason"] = reason 2133 + return result 2134 + 2135 + 2002 2136 def _record_from_evidence( 2003 2137 *, 2004 2138 evidence: BrainEvidenceRecord, ··· 2082 2216 raise BrainStateConflictError("brain runtime failure marker changed") 2083 2217 2084 2218 2219 + def begin_brain_prerequisite_renewal( 2220 + now: datetime, 2221 + *, 2222 + expected_fingerprint_sha256: str | None = None, 2223 + run_id: str | None = None, 2224 + journal_path: str | Path | None = None, 2225 + ) -> BrainPrerequisiteRenewalBeginResult: 2226 + """Begin a fenced SPP prerequisite-only renewal. 2227 + 2228 + This is deliberately narrower than ``begin_brain_refresh``: it preserves 2229 + same-fingerprint model evidence and refuses to start unless replacing only 2230 + ``lane_prerequisites`` can be made safe. 2231 + """ 2232 + 2233 + try: 2234 + now = _utc(now) 2235 + except ValueError as exc: 2236 + return _prerequisite_renewal_begin_result("unsafe", reason=str(exc)) 2237 + if expected_fingerprint_sha256 is not None: 2238 + try: 2239 + _validate_hex(expected_fingerprint_sha256, "expected_fingerprint_sha256") 2240 + except BrainStateValidationError: 2241 + return _prerequisite_renewal_begin_result( 2242 + "unsafe", reason="fingerprint_mismatch" 2243 + ) 2244 + path = brain_state_path(journal_path=journal_path) 2245 + lease = acquire_file_lease(brain_refresh_lease_path(journal_path=journal_path)) 2246 + if lease is None: 2247 + return _prerequisite_renewal_begin_result("busy", reason="lease_held") 2248 + try: 2249 + try: 2250 + _config, key, fingerprint = _load_fingerprint_for_write( 2251 + journal_path=journal_path 2252 + ) 2253 + except (CorruptConfigError, OSError, BrainStateValidationError) as exc: 2254 + lease.release() 2255 + return _prerequisite_renewal_begin_result("unsafe", reason=str(exc)) 2256 + if key is None or fingerprint is None or not fingerprint["ok"]: 2257 + lease.release() 2258 + return _prerequisite_renewal_begin_result( 2259 + "unsafe", reason="fingerprint_not_available" 2260 + ) 2261 + if fingerprint["active_lane"] != "spp": 2262 + lease.release() 2263 + return _prerequisite_renewal_begin_result("unsafe", reason="non_spp_lane") 2264 + fingerprint_sha = fingerprint["fingerprint_sha256"] 2265 + if fingerprint_sha is None or ( 2266 + expected_fingerprint_sha256 is not None 2267 + and fingerprint_sha != expected_fingerprint_sha256 2268 + ): 2269 + lease.release() 2270 + return _prerequisite_renewal_begin_result( 2271 + "unsafe", reason="fingerprint_mismatch" 2272 + ) 2273 + 2274 + run_id = run_id or uuid.uuid4().hex 2275 + expires_at = now + CHECKING_TTL 2276 + with hold_lock(path, mode=BRAIN_FILE_MODE): 2277 + try: 2278 + current = _read_record_unlocked(path) 2279 + except ( 2280 + OSError, 2281 + BrainStateValidationError, 2282 + MalformedDataError, 2283 + json.JSONDecodeError, 2284 + ): 2285 + lease.release() 2286 + return _prerequisite_renewal_begin_result( 2287 + "unsafe", reason="brain_record_unavailable" 2288 + ) 2289 + if current is None or current["fingerprint_sha256"] != fingerprint_sha: 2290 + lease.release() 2291 + return _prerequisite_renewal_begin_result( 2292 + "unsafe", reason="brain_record_missing" 2293 + ) 2294 + evidence = _safe_prerequisite_renewal_evidence(current, now) 2295 + if evidence is None: 2296 + lease.release() 2297 + return _prerequisite_renewal_begin_result( 2298 + "unsafe", reason="unsafe_evidence" 2299 + ) 2300 + revision = _next_revision(current) 2301 + marker_seen = _runtime_failure_marker_id(current) 2302 + checking: BrainCheckingRecord = { 2303 + "run_id": run_id, 2304 + "started_at": _iso(now), 2305 + "expires_at": _iso(expires_at), 2306 + "fingerprint_sha256": fingerprint_sha, 2307 + "checking_revision": revision, 2308 + "runtime_failure_marker_seen": marker_seen, 2309 + } 2310 + record = _record( 2311 + revision=revision, 2312 + aggregate_state="checking", 2313 + reason_code="brain_check_in_progress", 2314 + active_lane="spp", 2315 + active_provider=fingerprint["active_provider"], 2316 + active_model=fingerprint["active_model"], 2317 + fingerprint_sha256=fingerprint_sha, 2318 + checking=checking, 2319 + evidence=evidence, 2320 + runtime_failure_marker=current["runtime_failure_marker"], 2321 + diagnostic={}, 2322 + now=now, 2323 + ) 2324 + _write_record(path, record) 2325 + return _prerequisite_renewal_begin_result( 2326 + "started", 2327 + permit=BrainRefreshPermit( 2328 + run_id=run_id, 2329 + started_at=now, 2330 + expires_at=expires_at, 2331 + fingerprint_sha256=fingerprint_sha, 2332 + checking_revision=revision, 2333 + runtime_failure_marker_seen=marker_seen, 2334 + lease=lease, 2335 + ), 2336 + ) 2337 + except BaseException: 2338 + lease.release() 2339 + raise 2340 + 2341 + 2342 + def _validate_lane_prerequisite_component( 2343 + component: Mapping[str, Any], 2344 + ) -> BrainEvidenceComponent: 2345 + validated = _validate_component( 2346 + component, 2347 + "lane_prerequisites", 2348 + component_name="lane_prerequisites", 2349 + ) 2350 + if validated is None or validated["status"] == "not_attempted": 2351 + raise BrainStateValidationError( 2352 + "lane_prerequisites.status", 2353 + "prerequisite renewal requires ok or declared failure", 2354 + ) 2355 + return validated 2356 + 2357 + 2358 + def finish_brain_prerequisite_renewal( 2359 + permit: BrainRefreshPermit, 2360 + lane_prerequisites: Mapping[str, Any], 2361 + now: datetime, 2362 + *, 2363 + journal_path: str | Path | None = None, 2364 + ) -> BrainStateRecord: 2365 + now = _utc(now) 2366 + path = brain_state_path(journal_path=journal_path) 2367 + try: 2368 + component = _validate_lane_prerequisite_component(lane_prerequisites) 2369 + _config, key, fingerprint = _load_fingerprint_for_write( 2370 + journal_path=journal_path 2371 + ) 2372 + if key is None or fingerprint is None: 2373 + raise BrainStateConflictError("brain fingerprint key is unavailable") 2374 + with hold_lock(path, mode=BRAIN_FILE_MODE): 2375 + current = _read_record_unlocked(path) 2376 + _assert_finish_allowed(permit, current, now) 2377 + if fingerprint["fingerprint_sha256"] != permit.fingerprint_sha256: 2378 + raise BrainStateConflictError("brain fingerprint changed") 2379 + if fingerprint["active_lane"] != "spp": 2380 + raise BrainStateConflictError("brain lane changed") 2381 + assert current is not None 2382 + evidence = _safe_prerequisite_renewal_evidence(current, now) 2383 + if evidence is None: 2384 + raise BrainStateConflictError("brain prerequisite evidence is unsafe") 2385 + evidence["lane_prerequisites"] = component 2386 + record = _record_from_evidence( 2387 + evidence=evidence, 2388 + fingerprint=fingerprint, 2389 + revision=_next_revision(current), 2390 + now=now, 2391 + checking=None, 2392 + runtime_failure_marker=None, 2393 + ) 2394 + return _write_record(path, record) 2395 + finally: 2396 + permit.release() 2397 + 2398 + 2399 + def abandon_brain_prerequisite_renewal( 2400 + permit: BrainRefreshPermit, 2401 + reason_code: BrainReasonCode, 2402 + now: datetime, 2403 + *, 2404 + diagnostic: Mapping[str, BrainDiagnosticValue] | None = None, 2405 + journal_path: str | Path | None = None, 2406 + ) -> BrainStateRecord: 2407 + component = _component( 2408 + _component_status_for_reason(reason_code), 2409 + _utc(now), 2410 + reason_code=reason_code, 2411 + diagnostic=diagnostic, 2412 + ) 2413 + return finish_brain_prerequisite_renewal( 2414 + permit, 2415 + component, 2416 + now, 2417 + journal_path=journal_path, 2418 + ) 2419 + 2420 + 2085 2421 def finish_brain_refresh( 2086 2422 permit: BrainRefreshPermit, 2087 2423 outcome: BrainProbeOutcome, ··· 2320 2656 "BrainLaneId", 2321 2657 "BrainProbeOutcome", 2322 2658 "BrainProjection", 2659 + "BrainPrerequisiteRenewalBeginResult", 2660 + "BrainPrerequisiteRenewalStatus", 2323 2661 "BrainReasonCode", 2324 2662 "BrainRefreshPermit", 2325 2663 "BrainRuntimeFailureComponent", 2326 2664 "BrainRuntimeFailureMarker", 2327 2665 "BrainRuntimeFailureResult", 2666 + "BrainStateExpectedFingerprintStaleError", 2328 2667 "BrainStateConflictError", 2329 2668 "BrainStateInspection", 2330 2669 "BrainStateRecord", 2331 2670 "BrainStateValidationError", 2332 2671 "abandon_brain_refresh", 2672 + "abandon_brain_prerequisite_renewal", 2333 2673 "brain_fingerprint_key_path", 2334 2674 "brain_refresh_lease_path", 2335 2675 "brain_state_path", 2336 2676 "begin_brain_refresh", 2677 + "begin_brain_prerequisite_renewal", 2337 2678 "build_active_brain_fingerprint", 2338 2679 "derive_active_brain_lane", 2339 2680 "finish_brain_refresh", 2681 + "finish_brain_prerequisite_renewal", 2340 2682 "inspect_brain_state", 2341 2683 "project_brain_state", 2342 2684 "read_active_brain_fingerprint_sha256",
+9
solstone/think/services/spp_attest/nvgpu/appraise.py
··· 28 28 from solstone.think.services.spp_attest.tlv import GpuEnvelope 29 29 30 30 log = logging.getLogger(__name__) 31 + NVATTEST_TIMEOUT_S = 60.0 31 32 32 33 33 34 def appraise_gpu_leg( ··· 85 86 capture_output=True, 86 87 text=True, 87 88 check=False, 89 + timeout=NVATTEST_TIMEOUT_S, 88 90 ) 91 + except subprocess.TimeoutExpired as exc: 92 + _log_gpu_appraisal_failure( 93 + "gpu_appraisal_failed", 94 + exception_class=type(exc).__name__, 95 + stderr=exc.stderr, 96 + ) 97 + raise GpuAppraisalError("gpu_appraisal_failed") from exc 89 98 except OSError as exc: 90 99 _log_gpu_appraisal_failure( 91 100 "nvattest_unavailable",
+4 -7
solstone/think/services/spp_transport.py
··· 18 18 19 19 from OpenSSL import SSL 20 20 21 - from solstone.think.models import AttestationFailedError, AttestationStaleError 21 + from solstone.think.models import AttestationFailedError 22 22 from solstone.think.providers.nvattest_install import ( 23 23 ensure_nvattest_installed, 24 24 nvattest_cache_ready, ··· 260 260 ) 261 261 262 262 263 - def _reuse_or_raise_stale_locked(now: datetime) -> bool: 263 + def _reuse_or_teardown_stale_locked(now: datetime) -> bool: 264 264 state = spp.get_attestation_state() 265 265 if ( 266 266 state.session is not None ··· 274 274 and _transport_live_locked() 275 275 ): 276 276 _teardown_locked() 277 - raise AttestationStaleError( 278 - "the confidential attestation cadence lapsed (attestation_stale)" 279 - ) 280 277 return False 281 278 282 279 ··· 286 283 now = datetime.now(timezone.utc) 287 284 with _LOCK: 288 285 _CONFIDENTIAL_BLOCK = dict(block) 289 - if _reuse_or_raise_stale_locked(now): 286 + if _reuse_or_teardown_stale_locked(now): 290 287 return 291 288 292 289 nvattest_dir = _ensure_nvattest_for_attestation(block) 293 290 with _LOCK: 294 291 _CONFIDENTIAL_BLOCK = dict(block) 295 - if _reuse_or_raise_stale_locked(now): 292 + if _reuse_or_teardown_stale_locked(now): 296 293 return 297 294 _establish_and_record_locked(block, now, nvattest_dir=nvattest_dir) 298 295
+19
tests/services/test_spp_attest_cadence.py
··· 88 88 ) 89 89 90 90 91 + def test_attestation_session_verified_one_microsecond_before_each_boundary() -> None: 92 + assert ( 93 + _session(tpm_heartbeat_at=NOW - TPM_HEARTBEAT_INTERVAL).status( 94 + NOW - timedelta(microseconds=1) 95 + ) 96 + == "verified" 97 + ) 98 + assert ( 99 + _session(gpu_reattest_at=NOW - GPU_REATTEST_INTERVAL).status( 100 + NOW - timedelta(microseconds=1) 101 + ) 102 + == "verified" 103 + ) 104 + assert ( 105 + _session(started_at=NOW - SESSION_CAP).status(NOW - timedelta(microseconds=1)) 106 + == "verified" 107 + ) 108 + 109 + 91 110 def test_attestation_session_stale_when_tpm_heartbeat_lapses() -> None: 92 111 session = _session(tpm_heartbeat_at=NOW - TPM_HEARTBEAT_INTERVAL) 93 112
+39
tests/services/test_spp_attest_nvgpu.py
··· 396 396 assert "collector detail" not in message 397 397 398 398 399 + def test_nvattest_timeout_is_bounded_redacted_and_cleans_evidence( 400 + tmp_path: Path, 401 + monkeypatch: pytest.MonkeyPatch, 402 + caplog: pytest.LogCaptureFixture, 403 + ) -> None: 404 + nvattest_dir = _fake_nvattest_dir(tmp_path) 405 + observed: dict[str, Any] = {} 406 + marker = "timeout-secret-nonce_from_ar" 407 + 408 + def timeout_run(argv, **kwargs): 409 + observed["timeout"] = kwargs["timeout"] 410 + evidence_path = Path(argv[argv.index("--gpu-evidence-file") + 1]) 411 + observed["evidence_path"] = evidence_path 412 + assert evidence_path.exists() 413 + raise subprocess.TimeoutExpired(argv, kwargs["timeout"], stderr=marker) 414 + 415 + monkeypatch.setattr(appraise_module.subprocess, "run", timeout_run) 416 + caplog.set_level(logging.WARNING, logger=appraise_module.log.name) 417 + 418 + with pytest.raises(GpuAppraisalError) as exc_info: 419 + appraise_module.appraise_gpu_leg( 420 + _envelope(), 421 + _owner_nonce(), 422 + nvattest_dir=nvattest_dir, 423 + ) 424 + 425 + assert exc_info.value.reason == "gpu_appraisal_failed" 426 + assert observed["timeout"] == appraise_module.NVATTEST_TIMEOUT_S 427 + assert not observed["evidence_path"].exists() 428 + messages = [ 429 + record.getMessage() 430 + for record in caplog.records 431 + if "event=nvattest_gpu_appraisal_failed" in record.getMessage() 432 + ] 433 + assert len(messages) == 1 434 + assert "exception=TimeoutExpired" in messages[0] 435 + assert marker not in messages[0] 436 + 437 + 399 438 def test_bool_false_returncode_rejects( 400 439 tmp_path: Path, 401 440 monkeypatch: pytest.MonkeyPatch,
+117 -10
tests/services/test_spp_transport.py
··· 4 4 from __future__ import annotations 5 5 6 6 import json 7 + import threading 7 8 import time 8 9 from datetime import datetime, timedelta, timezone 9 10 from pathlib import Path ··· 13 14 import pytest 14 15 15 16 from solstone.think.journal_io.locking import hold_lock 16 - from solstone.think.models import AttestationFailedError, AttestationStaleError 17 + from solstone.think.models import AttestationFailedError 17 18 from solstone.think.providers import nvattest_install 18 19 from solstone.think.services import spp, spp_transport 19 20 from solstone.think.services.spp_attest.cadence import AttestationSession ··· 124 125 ) 125 126 126 127 127 - def test_verify_confidential_attestation_reuses_then_raises_stale_once_then_reestablishes( 128 + def test_verify_confidential_attestation_reuses_then_rotates_stale_session_inline( 128 129 tmp_path: Path, 129 130 monkeypatch: pytest.MonkeyPatch, 130 131 ) -> None: ··· 145 146 assert spp.get_attestation_state().session is not None 146 147 147 148 spp.record_attestation_verified(_stale_session(verdict)) 148 - with pytest.raises(AttestationStaleError): 149 - spp_transport.verify_confidential_attestation(block) 150 - 151 - state = spp.get_attestation_state() 152 - assert state.session is not None 153 - assert state.session.status(datetime.now(timezone.utc)) == "stale" 154 - assert establish.call_count == 1 155 - 156 149 spp_transport.verify_confidential_attestation(block) 157 150 158 151 assert establish.call_count == 2 ··· 161 154 spp.get_attestation_state().session.status(datetime.now(timezone.utc)) 162 155 == "verified" 163 156 ) 157 + spp_transport.verify_confidential_attestation(block) 158 + assert establish.call_count == 2 159 + 160 + 161 + def test_concurrent_stale_rotation_establishes_one_replacement( 162 + tmp_path: Path, 163 + monkeypatch: pytest.MonkeyPatch, 164 + ) -> None: 165 + block = _write_confidential_config(tmp_path, monkeypatch) 166 + _patch_listener(monkeypatch) 167 + old_verdict = object() 168 + old_channel = _FakeChannel(old_verdict) 169 + spp.record_attestation_verified(_stale_session(old_verdict)) 170 + with spp_transport._LOCK: 171 + spp_transport._LISTENER = _FakeListener() 172 + spp_transport._LISTENER_THREAD = _AliveThread() 173 + spp_transport._FORWARDER_BASE_URL = "http://127.0.0.1:1111" 174 + spp_transport._POOL[:] = [old_channel] 175 + 176 + ensure_barrier = threading.Barrier(2) 177 + ensure_calls = 0 178 + 179 + def delayed_ensure(_block): 180 + nonlocal ensure_calls 181 + ensure_calls += 1 182 + ensure_barrier.wait(timeout=2) 183 + return Path("/tmp/solstone-nvattest-test") 184 + 185 + establish_started = threading.Event() 186 + release_establish = threading.Event() 187 + 188 + def delayed_establish(*_args, **kwargs): 189 + establish_started.set() 190 + assert release_establish.wait(timeout=2) 191 + return _FakeChannel(object(), epoch=kwargs["epoch"]) 192 + 193 + establish = Mock(side_effect=delayed_establish) 194 + monkeypatch.setattr( 195 + spp_transport, 196 + "_ensure_nvattest_for_attestation", 197 + delayed_ensure, 198 + ) 199 + monkeypatch.setattr(spp_transport, "establish_attested_channel", establish) 200 + 201 + errors: list[BaseException] = [] 202 + 203 + def verify() -> None: 204 + try: 205 + spp_transport.verify_confidential_attestation(block) 206 + except BaseException as exc: # pragma: no cover - surfaced below 207 + errors.append(exc) 208 + 209 + first = threading.Thread(target=verify) 210 + second = threading.Thread(target=verify) 211 + first.start() 212 + second.start() 213 + assert establish_started.wait(timeout=2) 214 + assert first.is_alive() 215 + assert second.is_alive() 216 + release_establish.set() 217 + first.join(timeout=2) 218 + second.join(timeout=2) 219 + 220 + assert errors == [] 221 + assert not first.is_alive() 222 + assert not second.is_alive() 223 + assert ensure_calls == 2 224 + assert establish.call_count == 1 225 + assert old_channel.closed is True 226 + assert spp.get_attestation_state().session is not None 164 227 165 228 166 229 def test_confidential_egress_base_url_returns_forwarder_not_configured_endpoint( ··· 236 299 237 300 with pytest.raises(AttestationFailedError): 238 301 spp_transport.confidential_forwarder_base_url() 302 + 303 + 304 + def test_stale_rotation_failure_returns_no_forwarder_and_later_recovers( 305 + tmp_path: Path, 306 + monkeypatch: pytest.MonkeyPatch, 307 + ) -> None: 308 + block = _write_confidential_config(tmp_path, monkeypatch) 309 + _patch_listener(monkeypatch) 310 + old_verdict = object() 311 + spp.record_attestation_verified(_stale_session(old_verdict)) 312 + old_channel = _FakeChannel(old_verdict) 313 + spp_transport._POOL[:] = [old_channel] 314 + establish = Mock( 315 + side_effect=[ 316 + RatlsChannelError("gateway_unreachable"), 317 + _FakeChannel(object()), 318 + ] 319 + ) 320 + monkeypatch.setattr(spp_transport, "establish_attested_channel", establish) 321 + pump = Mock(return_value="local_closed") 322 + monkeypatch.setattr(spp_transport, "_pump", pump) 323 + 324 + with pytest.raises(AttestationFailedError): 325 + spp_transport.confidential_egress_base_url(block["endpoint_url"]) 326 + assert old_channel.closed is True 327 + assert spp_transport._FORWARDER_BASE_URL is None 328 + assert spp_transport._LISTENER is None 329 + assert spp_transport._POOL == [] 330 + assert spp_transport._ACTIVE == set() 331 + assert spp.get_attestation_state().session is None 332 + assert spp.get_attestation_state().failure is not None 333 + local = _FakeLocal() 334 + spp_transport._handle_loopback_connection(local) 335 + assert local.closed is True 336 + pump.assert_not_called() 337 + 338 + assert ( 339 + spp_transport.confidential_egress_base_url(block["endpoint_url"]) 340 + == "http://127.0.0.1:4567" 341 + ) 342 + assert establish.call_count == 2 343 + assert spp.get_attestation_state().session is not None 344 + assert spp_transport._POOL 345 + assert all(channel is not old_channel for channel in spp_transport._POOL) 239 346 240 347 241 348 def test_confidential_probe_status_reads_state_without_attestation(
+229 -2
tests/test_brain_cli.py
··· 8 8 import json 9 9 import logging 10 10 import sys 11 - from datetime import datetime, timezone 11 + from datetime import datetime, timedelta, timezone 12 12 from pathlib import Path 13 13 from types import SimpleNamespace 14 14 from typing import Any ··· 145 145 146 146 147 147 def _args(**kwargs: Any) -> argparse.Namespace: 148 - defaults = {"json": False, "expected_fingerprint": None} 148 + defaults = { 149 + "json": False, 150 + "expected_fingerprint": None, 151 + "expected_active_fingerprint": False, 152 + "expect_active_fingerprint_absent": False, 153 + } 149 154 defaults.update(kwargs) 150 155 return argparse.Namespace(**defaults) 151 156 ··· 641 646 assert record["evidence"]["cogitate"]["reason_code"] == "local_runtime_not_ready" 642 647 643 648 649 + def test_renew_prerequisites_updates_spp_only_without_model_probes( 650 + brain_journal: Path, 651 + monkeypatch: pytest.MonkeyPatch, 652 + capsys: pytest.CaptureFixture[str], 653 + ) -> None: 654 + config = _endpoint_config(confidential=True) 655 + prior_now = NOW - timedelta(minutes=1) 656 + _write_ready_record(brain_journal, config, now=prior_now) 657 + before = json.loads(brain_state_path(journal_path=brain_journal).read_text()) 658 + expected = brain_state_module.read_active_brain_fingerprint_sha256( 659 + journal_path=brain_journal 660 + ) 661 + assert expected is not None 662 + _forbid_provider_calls(monkeypatch) 663 + monkeypatch.setattr( 664 + brain_cli, 665 + "_spp_prerequisite", 666 + lambda now: ( 667 + brain_cli._ok_component(now, expires_at=now + timedelta(minutes=10)), 668 + None, 669 + ), 670 + ) 671 + 672 + code = brain_cli._run_renew_prerequisites( 673 + _args(json=True, expected_fingerprint=expected) 674 + ) 675 + payload = json.loads(capsys.readouterr().out) 676 + record = json.loads(brain_state_path(journal_path=brain_journal).read_text()) 677 + 678 + assert code == 0 679 + assert payload["aggregate_state"] == "ready" 680 + assert record["evidence"]["configuration"] == before["evidence"]["configuration"] 681 + assert record["evidence"]["generate"] == before["evidence"]["generate"] 682 + assert record["evidence"]["cogitate"] == before["evidence"]["cogitate"] 683 + assert record["evidence"]["lane_prerequisites"]["observed_at"] == NOW.isoformat() 684 + 685 + 686 + def test_renew_prerequisites_busy_does_not_fallback( 687 + brain_journal: Path, 688 + monkeypatch: pytest.MonkeyPatch, 689 + capsys: pytest.CaptureFixture[str], 690 + ) -> None: 691 + _write_ready_record(brain_journal, _endpoint_config(confidential=True)) 692 + lease = acquire_file_lease(brain_refresh_lease_path(journal_path=brain_journal)) 693 + assert lease is not None 694 + monkeypatch.setattr( 695 + brain_cli, 696 + "_run_refresh", 697 + lambda _args: pytest.fail("busy must not fallback to full refresh"), 698 + ) 699 + _forbid_provider_calls(monkeypatch) 700 + try: 701 + code = brain_cli._run_renew_prerequisites(_args(json=True)) 702 + finally: 703 + lease.release() 704 + payload = json.loads(capsys.readouterr().out) 705 + 706 + assert code == 3 707 + assert payload["reason_code"] == "busy" 708 + 709 + 710 + def test_renew_prerequisites_unsafe_dispatches_full_refresh( 711 + brain_journal: Path, 712 + monkeypatch: pytest.MonkeyPatch, 713 + ) -> None: 714 + _write_config(brain_journal, _cloud_config()) 715 + calls: list[argparse.Namespace] = [] 716 + 717 + def fake_refresh(args: argparse.Namespace) -> int: 718 + calls.append(args) 719 + return 7 720 + 721 + monkeypatch.setattr(brain_cli, "_run_refresh", fake_refresh) 722 + 723 + assert brain_cli._run_renew_prerequisites(_args(json=True)) == 7 724 + assert len(calls) == 1 725 + assert calls[0].expected_active_fingerprint is True 726 + 727 + 728 + def test_renew_prerequisites_expected_mismatch_writes_nothing( 729 + brain_journal: Path, 730 + monkeypatch: pytest.MonkeyPatch, 731 + capsys: pytest.CaptureFixture[str], 732 + ) -> None: 733 + _write_ready_record(brain_journal, _endpoint_config(confidential=True)) 734 + before = _health_snapshot(brain_journal) 735 + _forbid_provider_calls(monkeypatch) 736 + 737 + code = brain_cli._run_renew_prerequisites( 738 + _args(json=True, expected_fingerprint="f" * 64) 739 + ) 740 + payload = json.loads(capsys.readouterr().out) 741 + 742 + assert code == 3 743 + assert payload["reason_code"] == "stale_expected_fingerprint" 744 + assert _health_snapshot(brain_journal) == before 745 + 746 + 644 747 def test_refresh_cloud_missing_key_skips_inference( 645 748 brain_journal: Path, 646 749 monkeypatch: pytest.MonkeyPatch, ··· 963 1066 964 1067 assert code == 3 965 1068 assert payload["reason_code"] == "stale_expected_fingerprint" 1069 + 1070 + 1071 + def test_refresh_expected_active_fingerprint_mismatch_writes_nothing( 1072 + brain_journal: Path, 1073 + monkeypatch: pytest.MonkeyPatch, 1074 + capsys: pytest.CaptureFixture[str], 1075 + ) -> None: 1076 + original = _endpoint_config(confidential=True) 1077 + changed = _endpoint_config(confidential=True) 1078 + changed["providers"]["local"]["served_model_id"] = "new-served-model" 1079 + changed["services"]["confidential"]["served_model_id"] = "new-served-model" 1080 + _write_ready_record(brain_journal, original) 1081 + expected = brain_state_module.read_active_brain_fingerprint_sha256( 1082 + journal_path=brain_journal 1083 + ) 1084 + assert expected is not None 1085 + before = _health_snapshot(brain_journal) 1086 + _write_config(brain_journal, changed) 1087 + _forbid_provider_calls(monkeypatch) 1088 + 1089 + code = brain_cli._run_refresh( 1090 + _args( 1091 + json=True, 1092 + expected_fingerprint=expected, 1093 + expected_active_fingerprint=True, 1094 + ) 1095 + ) 1096 + payload = json.loads(capsys.readouterr().out) 1097 + 1098 + assert code == 3 1099 + assert payload["reason_code"] == "stale_expected_fingerprint" 1100 + assert _health_snapshot(brain_journal) == before 1101 + 1102 + 1103 + def test_refresh_expected_active_fingerprint_race_before_begin_writes_nothing( 1104 + brain_journal: Path, 1105 + monkeypatch: pytest.MonkeyPatch, 1106 + capsys: pytest.CaptureFixture[str], 1107 + ) -> None: 1108 + original = _endpoint_config(confidential=True) 1109 + changed = _endpoint_config(confidential=True) 1110 + changed["providers"]["local"]["served_model_id"] = "race-served-model" 1111 + changed["services"]["confidential"]["served_model_id"] = "race-served-model" 1112 + _write_ready_record(brain_journal, original) 1113 + expected = brain_state_module.read_active_brain_fingerprint_sha256( 1114 + journal_path=brain_journal 1115 + ) 1116 + assert expected is not None 1117 + before = _health_snapshot(brain_journal) 1118 + real_match = brain_cli._expected_refresh_fingerprint_matches 1119 + 1120 + def match_then_switch(args: argparse.Namespace, value: str) -> bool: 1121 + assert real_match(args, value) 1122 + _write_config(brain_journal, changed) 1123 + return True 1124 + 1125 + monkeypatch.setattr( 1126 + brain_cli, 1127 + "_expected_refresh_fingerprint_matches", 1128 + match_then_switch, 1129 + ) 1130 + _forbid_provider_calls(monkeypatch) 1131 + 1132 + code = brain_cli._run_refresh( 1133 + _args( 1134 + json=True, 1135 + expected_fingerprint=expected, 1136 + expected_active_fingerprint=True, 1137 + ) 1138 + ) 1139 + payload = json.loads(capsys.readouterr().out) 1140 + 1141 + assert code == 3 1142 + assert payload["reason_code"] == "stale_expected_fingerprint" 1143 + assert _health_snapshot(brain_journal) == before 1144 + 1145 + 1146 + def test_refresh_expected_absent_fingerprint_bootstraps_spp( 1147 + brain_journal: Path, 1148 + monkeypatch: pytest.MonkeyPatch, 1149 + capsys: pytest.CaptureFixture[str], 1150 + ) -> None: 1151 + _write_config(brain_journal, _endpoint_config(confidential=True)) 1152 + assert not brain_fingerprint_key_path(journal_path=brain_journal).exists() 1153 + monkeypatch.setattr( 1154 + brain_cli, 1155 + "_spp_prerequisite", 1156 + lambda now: ( 1157 + brain_cli._ok_component(now, expires_at=now + timedelta(minutes=10)), 1158 + None, 1159 + ), 1160 + ) 1161 + generate_calls, cogitate_calls = _patch_generate_and_cogitate(monkeypatch) 1162 + 1163 + code = brain_cli._run_refresh( 1164 + _args(json=True, expect_active_fingerprint_absent=True) 1165 + ) 1166 + payload = json.loads(capsys.readouterr().out) 1167 + 1168 + assert code == 0 1169 + assert payload["aggregate_state"] == "ready" 1170 + assert payload["lane"] == "spp" 1171 + assert brain_fingerprint_key_path(journal_path=brain_journal).exists() 1172 + assert len(generate_calls) == 1 1173 + assert len(cogitate_calls) == 1 1174 + 1175 + 1176 + def test_refresh_expected_absent_fingerprint_stale_when_key_appears( 1177 + brain_journal: Path, 1178 + monkeypatch: pytest.MonkeyPatch, 1179 + capsys: pytest.CaptureFixture[str], 1180 + ) -> None: 1181 + _write_ready_record(brain_journal, _endpoint_config(confidential=True)) 1182 + before = _health_snapshot(brain_journal) 1183 + _forbid_provider_calls(monkeypatch) 1184 + 1185 + code = brain_cli._run_refresh( 1186 + _args(json=True, expect_active_fingerprint_absent=True) 1187 + ) 1188 + payload = json.loads(capsys.readouterr().out) 1189 + 1190 + assert code == 3 1191 + assert payload["reason_code"] == "stale_expected_fingerprint" 1192 + assert _health_snapshot(brain_journal) == before 966 1193 967 1194 968 1195 def test_expected_fingerprint_match_proceeds_and_ready_short_circuits(
+390
tests/test_brain_state.py
··· 27 27 DEFAULT_READY_EVIDENCE_TTL, 28 28 BrainProbeOutcome, 29 29 BrainStateConflictError, 30 + BrainStateExpectedFingerprintStaleError, 30 31 BrainStateValidationError, 32 + abandon_brain_prerequisite_renewal, 31 33 abandon_brain_refresh, 34 + begin_brain_prerequisite_renewal, 32 35 begin_brain_refresh, 33 36 brain_fingerprint_key_path, 34 37 brain_state_path, 35 38 build_active_brain_fingerprint, 39 + finish_brain_prerequisite_renewal, 36 40 finish_brain_refresh, 37 41 inspect_brain_state, 38 42 project_brain_state, ··· 1050 1054 assert result["rejected_reason"] == "fingerprint_mismatch" 1051 1055 assert state_path.read_bytes() == prior_bytes 1052 1056 assert _read_raw_record(tmp_path)["revision"] == prior_revision 1057 + 1058 + 1059 + def test_begin_refresh_expected_active_fingerprint_stales_after_switch( 1060 + tmp_path: Path, 1061 + ) -> None: 1062 + original = _spp_config(account_id="acct-a") 1063 + _write_ready_record(tmp_path, original) 1064 + expected = _current_fingerprint(tmp_path, original) 1065 + before_record = brain_state_path(journal_path=tmp_path).read_bytes() 1066 + before_key = brain_fingerprint_key_path(journal_path=tmp_path).read_bytes() 1067 + _write_config(tmp_path, _spp_config(account_id="acct-b")) 1068 + 1069 + with pytest.raises(BrainStateExpectedFingerprintStaleError): 1070 + begin_brain_refresh( 1071 + NOW + timedelta(seconds=1), 1072 + expected_active_fingerprint_sha256=expected, 1073 + journal_path=tmp_path, 1074 + ) 1075 + 1076 + assert brain_state_path(journal_path=tmp_path).read_bytes() == before_record 1077 + assert brain_fingerprint_key_path(journal_path=tmp_path).read_bytes() == before_key 1078 + 1079 + 1080 + def test_begin_refresh_expected_absent_fingerprint_bootstraps_key( 1081 + tmp_path: Path, 1082 + ) -> None: 1083 + _write_config(tmp_path, _spp_config()) 1084 + assert not brain_fingerprint_key_path(journal_path=tmp_path).exists() 1085 + 1086 + permit = begin_brain_refresh( 1087 + NOW, 1088 + expect_active_fingerprint_absent=True, 1089 + journal_path=tmp_path, 1090 + ) 1091 + 1092 + assert permit is not None 1093 + assert brain_fingerprint_key_path(journal_path=tmp_path).exists() 1094 + record = finish_brain_refresh( 1095 + permit, 1096 + _ready_outcome(NOW + timedelta(seconds=1)), 1097 + NOW + timedelta(seconds=1), 1098 + journal_path=tmp_path, 1099 + ) 1100 + assert record["aggregate_state"] == "ready" 1101 + assert record["active_lane"] == "spp" 1102 + 1103 + 1104 + def test_begin_refresh_expected_absent_fingerprint_stales_when_key_exists( 1105 + tmp_path: Path, 1106 + ) -> None: 1107 + _write_ready_record(tmp_path, _spp_config()) 1108 + before_record = brain_state_path(journal_path=tmp_path).read_bytes() 1109 + before_key = brain_fingerprint_key_path(journal_path=tmp_path).read_bytes() 1110 + 1111 + with pytest.raises(BrainStateExpectedFingerprintStaleError): 1112 + begin_brain_refresh( 1113 + NOW + timedelta(seconds=1), 1114 + expect_active_fingerprint_absent=True, 1115 + journal_path=tmp_path, 1116 + ) 1117 + 1118 + assert brain_state_path(journal_path=tmp_path).read_bytes() == before_record 1119 + assert brain_fingerprint_key_path(journal_path=tmp_path).read_bytes() == before_key 1120 + 1121 + 1122 + def test_prerequisite_renewal_preserves_same_fingerprint_model_evidence( 1123 + tmp_path: Path, 1124 + ) -> None: 1125 + config = _spp_config() 1126 + _write_ready_record(tmp_path, config) 1127 + before = _read_raw_record(tmp_path) 1128 + expected = _current_fingerprint(tmp_path, config) 1129 + begin = begin_brain_prerequisite_renewal( 1130 + NOW + timedelta(minutes=1), 1131 + expected_fingerprint_sha256=expected, 1132 + journal_path=tmp_path, 1133 + ) 1134 + 1135 + assert begin["status"] == "started" 1136 + checking = _read_raw_record(tmp_path) 1137 + assert checking["aggregate_state"] == "checking" 1138 + assert checking["evidence"]["generate"] == before["evidence"]["generate"] 1139 + assert checking["evidence"]["cogitate"] == before["evidence"]["cogitate"] 1140 + 1141 + finish_now = NOW + timedelta(minutes=2) 1142 + record = finish_brain_prerequisite_renewal( 1143 + begin["permit"], 1144 + _component(finish_now, expires_at=finish_now + timedelta(minutes=10)), 1145 + finish_now, 1146 + journal_path=tmp_path, 1147 + ) 1148 + 1149 + assert record["aggregate_state"] == "ready" 1150 + assert record["revision"] == before["revision"] + 2 1151 + assert record["evidence"]["configuration"] == before["evidence"]["configuration"] 1152 + assert record["evidence"]["generate"] == before["evidence"]["generate"] 1153 + assert record["evidence"]["cogitate"] == before["evidence"]["cogitate"] 1154 + assert ( 1155 + record["evidence"]["lane_prerequisites"]["observed_at"] 1156 + == finish_now.isoformat() 1157 + ) 1158 + 1159 + 1160 + def test_prerequisite_renewal_refuses_expired_model_evidence(tmp_path: Path) -> None: 1161 + config = _spp_config() 1162 + _write_ready_record(tmp_path, config) 1163 + raw = _read_raw_record(tmp_path) 1164 + raw["evidence"]["generate"]["expires_at"] = (NOW - timedelta(seconds=1)).isoformat() 1165 + atomic_replace(brain_state_path(journal_path=tmp_path), json.dumps(raw), mode=0o600) 1166 + expected = _current_fingerprint(tmp_path, config) 1167 + 1168 + result = begin_brain_prerequisite_renewal( 1169 + NOW, 1170 + expected_fingerprint_sha256=expected, 1171 + journal_path=tmp_path, 1172 + ) 1173 + 1174 + assert result["status"] == "unsafe" 1175 + assert _read_raw_record(tmp_path)["revision"] == raw["revision"] 1176 + 1177 + 1178 + def test_prerequisite_renewal_reports_busy_when_refresh_lease_is_held( 1179 + tmp_path: Path, 1180 + ) -> None: 1181 + config = _spp_config() 1182 + _write_ready_record(tmp_path, config) 1183 + holder = begin_brain_refresh(NOW + timedelta(seconds=1), journal_path=tmp_path) 1184 + assert holder is not None 1185 + try: 1186 + result = begin_brain_prerequisite_renewal( 1187 + NOW + timedelta(seconds=2), 1188 + expected_fingerprint_sha256=_current_fingerprint(tmp_path, config), 1189 + journal_path=tmp_path, 1190 + ) 1191 + finally: 1192 + holder.release() 1193 + 1194 + assert result["status"] == "busy" 1195 + 1196 + 1197 + def test_prerequisite_renewal_refuses_non_spp_lane(tmp_path: Path) -> None: 1198 + config = _cloud_config() 1199 + _write_ready_record(tmp_path, config) 1200 + before = brain_state_path(journal_path=tmp_path).read_bytes() 1201 + 1202 + result = begin_brain_prerequisite_renewal( 1203 + NOW + timedelta(seconds=1), 1204 + expected_fingerprint_sha256=_current_fingerprint(tmp_path, config), 1205 + journal_path=tmp_path, 1206 + ) 1207 + 1208 + assert result["status"] == "unsafe" 1209 + assert brain_state_path(journal_path=tmp_path).read_bytes() == before 1210 + 1211 + 1212 + @pytest.mark.parametrize( 1213 + "mutation", 1214 + ( 1215 + lambda raw: raw["evidence"].__setitem__("generate", None), 1216 + lambda raw: raw["evidence"]["generate"].__setitem__("status", "failed"), 1217 + lambda raw: raw["evidence"]["generate"].__setitem__("expires_at", "not-time"), 1218 + ), 1219 + ) 1220 + def test_prerequisite_renewal_refuses_missing_malformed_or_non_ok_model_evidence( 1221 + tmp_path: Path, 1222 + mutation, 1223 + ) -> None: 1224 + config = _spp_config() 1225 + _write_ready_record(tmp_path, config) 1226 + raw = _read_raw_record(tmp_path) 1227 + mutation(raw) 1228 + atomic_replace(brain_state_path(journal_path=tmp_path), json.dumps(raw), mode=0o600) 1229 + before = brain_state_path(journal_path=tmp_path).read_bytes() 1230 + 1231 + result = begin_brain_prerequisite_renewal( 1232 + NOW + timedelta(seconds=1), 1233 + expected_fingerprint_sha256=_current_fingerprint(tmp_path, config), 1234 + journal_path=tmp_path, 1235 + ) 1236 + 1237 + assert result["status"] == "unsafe" 1238 + assert brain_state_path(journal_path=tmp_path).read_bytes() == before 1239 + 1240 + 1241 + def test_prerequisite_renewal_expected_fingerprint_mismatch_is_no_write( 1242 + tmp_path: Path, 1243 + ) -> None: 1244 + config = _spp_config() 1245 + _write_ready_record(tmp_path, config) 1246 + before = brain_state_path(journal_path=tmp_path).read_bytes() 1247 + 1248 + result = begin_brain_prerequisite_renewal( 1249 + NOW + timedelta(seconds=1), 1250 + expected_fingerprint_sha256="0" * 64, 1251 + journal_path=tmp_path, 1252 + ) 1253 + 1254 + assert result["status"] == "unsafe" 1255 + assert result["reason"] == "fingerprint_mismatch" 1256 + assert brain_state_path(journal_path=tmp_path).read_bytes() == before 1257 + 1258 + 1259 + def test_prerequisite_renewal_recovers_orphaned_checking_after_lease_released( 1260 + tmp_path: Path, 1261 + ) -> None: 1262 + config = _spp_config() 1263 + _write_ready_record(tmp_path, config) 1264 + expected = _current_fingerprint(tmp_path, config) 1265 + first = begin_brain_prerequisite_renewal( 1266 + NOW + timedelta(seconds=1), 1267 + expected_fingerprint_sha256=expected, 1268 + journal_path=tmp_path, 1269 + ) 1270 + assert first["status"] == "started" 1271 + first["permit"].release() 1272 + orphaned = _read_raw_record(tmp_path) 1273 + assert orphaned["aggregate_state"] == "checking" 1274 + 1275 + second = begin_brain_prerequisite_renewal( 1276 + NOW + timedelta(seconds=2), 1277 + expected_fingerprint_sha256=expected, 1278 + journal_path=tmp_path, 1279 + ) 1280 + assert second["status"] == "started" 1281 + recovered = _read_raw_record(tmp_path) 1282 + assert recovered["revision"] == orphaned["revision"] + 1 1283 + assert recovered["checking"]["run_id"] != orphaned["checking"]["run_id"] 1284 + second["permit"].release() 1285 + 1286 + 1287 + def test_prerequisite_renewal_expired_permit_conflicts_and_releases( 1288 + tmp_path: Path, 1289 + ) -> None: 1290 + config = _spp_config() 1291 + _write_ready_record(tmp_path, config) 1292 + expected = _current_fingerprint(tmp_path, config) 1293 + begin = begin_brain_prerequisite_renewal( 1294 + NOW, 1295 + expected_fingerprint_sha256=expected, 1296 + journal_path=tmp_path, 1297 + ) 1298 + assert begin["status"] == "started" 1299 + permit = begin["permit"] 1300 + 1301 + with pytest.raises(BrainStateConflictError): 1302 + finish_brain_prerequisite_renewal( 1303 + permit, 1304 + _component( 1305 + permit.expires_at, expires_at=permit.expires_at + timedelta(minutes=10) 1306 + ), 1307 + permit.expires_at, 1308 + journal_path=tmp_path, 1309 + ) 1310 + 1311 + retry = begin_brain_prerequisite_renewal( 1312 + NOW + timedelta(seconds=1), 1313 + expected_fingerprint_sha256=expected, 1314 + journal_path=tmp_path, 1315 + ) 1316 + assert retry["status"] == "started" 1317 + retry["permit"].release() 1318 + 1319 + 1320 + def test_prerequisite_renewal_conflicts_on_revision_drift_and_releases( 1321 + tmp_path: Path, 1322 + ) -> None: 1323 + config = _spp_config() 1324 + _write_ready_record(tmp_path, config) 1325 + expected = _current_fingerprint(tmp_path, config) 1326 + begin = begin_brain_prerequisite_renewal( 1327 + NOW, 1328 + expected_fingerprint_sha256=expected, 1329 + journal_path=tmp_path, 1330 + ) 1331 + assert begin["status"] == "started" 1332 + raw = _read_raw_record(tmp_path) 1333 + raw["revision"] += 1 1334 + raw["checking"]["checking_revision"] = raw["revision"] 1335 + atomic_replace(brain_state_path(journal_path=tmp_path), json.dumps(raw), mode=0o600) 1336 + 1337 + with pytest.raises(BrainStateConflictError): 1338 + finish_brain_prerequisite_renewal( 1339 + begin["permit"], 1340 + _component(NOW + timedelta(seconds=1)), 1341 + NOW + timedelta(seconds=1), 1342 + journal_path=tmp_path, 1343 + ) 1344 + 1345 + retry = begin_brain_prerequisite_renewal( 1346 + NOW + timedelta(seconds=2), 1347 + expected_fingerprint_sha256=expected, 1348 + journal_path=tmp_path, 1349 + ) 1350 + assert retry["status"] == "started" 1351 + retry["permit"].release() 1352 + 1353 + 1354 + def test_prerequisite_renewal_conflicts_on_runtime_marker_drift_and_releases( 1355 + tmp_path: Path, 1356 + ) -> None: 1357 + config = _spp_config() 1358 + _write_ready_record(tmp_path, config) 1359 + expected = _current_fingerprint(tmp_path, config) 1360 + begin = begin_brain_prerequisite_renewal( 1361 + NOW, 1362 + expected_fingerprint_sha256=expected, 1363 + journal_path=tmp_path, 1364 + ) 1365 + assert begin["status"] == "started" 1366 + raw = _read_raw_record(tmp_path) 1367 + raw["checking"]["runtime_failure_marker_seen"] = "changed-marker" 1368 + atomic_replace(brain_state_path(journal_path=tmp_path), json.dumps(raw), mode=0o600) 1369 + 1370 + with pytest.raises(BrainStateConflictError): 1371 + finish_brain_prerequisite_renewal( 1372 + begin["permit"], 1373 + _component(NOW + timedelta(seconds=1)), 1374 + NOW + timedelta(seconds=1), 1375 + journal_path=tmp_path, 1376 + ) 1377 + 1378 + retry = begin_brain_prerequisite_renewal( 1379 + NOW + timedelta(seconds=2), 1380 + expected_fingerprint_sha256=expected, 1381 + journal_path=tmp_path, 1382 + ) 1383 + assert retry["status"] == "started" 1384 + retry["permit"].release() 1385 + 1386 + 1387 + def test_prerequisite_renewal_conflicts_on_fingerprint_drift(tmp_path: Path) -> None: 1388 + config = _spp_config(account_id="acct-a") 1389 + _write_ready_record(tmp_path, config) 1390 + expected = _current_fingerprint(tmp_path, config) 1391 + begin = begin_brain_prerequisite_renewal( 1392 + NOW, 1393 + expected_fingerprint_sha256=expected, 1394 + journal_path=tmp_path, 1395 + ) 1396 + assert begin["status"] == "started" 1397 + _write_config(tmp_path, _spp_config(account_id="acct-b")) 1398 + 1399 + with pytest.raises(BrainStateConflictError): 1400 + finish_brain_prerequisite_renewal( 1401 + begin["permit"], 1402 + _component(NOW, expires_at=NOW + timedelta(minutes=10)), 1403 + NOW, 1404 + journal_path=tmp_path, 1405 + ) 1406 + 1407 + retry = begin_brain_prerequisite_renewal( 1408 + NOW + timedelta(seconds=1), 1409 + expected_fingerprint_sha256=_current_fingerprint( 1410 + tmp_path, _spp_config(account_id="acct-b") 1411 + ), 1412 + journal_path=tmp_path, 1413 + ) 1414 + assert retry["status"] == "unsafe" 1415 + 1416 + 1417 + def test_prerequisite_renewal_abandon_records_failure_without_losing_model_evidence( 1418 + tmp_path: Path, 1419 + ) -> None: 1420 + config = _spp_config() 1421 + _write_ready_record(tmp_path, config) 1422 + before = _read_raw_record(tmp_path) 1423 + begin = begin_brain_prerequisite_renewal( 1424 + NOW, 1425 + expected_fingerprint_sha256=_current_fingerprint(tmp_path, config), 1426 + journal_path=tmp_path, 1427 + ) 1428 + assert begin["status"] == "started" 1429 + 1430 + record = abandon_brain_prerequisite_renewal( 1431 + begin["permit"], 1432 + "probe_internal_error", 1433 + NOW + timedelta(seconds=1), 1434 + journal_path=tmp_path, 1435 + ) 1436 + 1437 + assert record["reason_code"] == "probe_internal_error" 1438 + assert record["evidence"]["lane_prerequisites"]["reason_code"] == ( 1439 + "probe_internal_error" 1440 + ) 1441 + assert record["evidence"]["generate"] == before["evidence"]["generate"] 1442 + assert record["evidence"]["cogitate"] == before["evidence"]["cogitate"] 1053 1443 1054 1444 1055 1445 def test_key_replacement_invalidates_prior_ready_record(tmp_path: Path) -> None:
+142 -2
tests/test_brain_state_multiprocess.py
··· 3 3 4 4 from __future__ import annotations 5 5 6 + import hashlib 6 7 import json 7 8 import os 8 9 import subprocess 9 10 import sys 10 11 import time 11 - from datetime import datetime, timezone 12 + from datetime import datetime, timedelta, timezone 12 13 from pathlib import Path 13 14 14 - from solstone.think.providers.brain_state import inspect_brain_state 15 + from solstone.think.models import LOCAL_MODEL 16 + from solstone.think.providers.brain_state import ( 17 + DEFAULT_READY_EVIDENCE_TTL, 18 + begin_brain_refresh, 19 + finish_brain_refresh, 20 + inspect_brain_state, 21 + ) 15 22 16 23 NOW = datetime(2026, 1, 2, 3, 4, 5, tzinfo=timezone.utc) 17 24 ··· 36 43 ) 37 44 38 45 46 + def _write_spp_config(journal: Path) -> None: 47 + credential = "endpoint-secret" 48 + path = journal / "config" / "journal.json" 49 + path.parent.mkdir(parents=True, exist_ok=True) 50 + path.write_text( 51 + json.dumps( 52 + { 53 + "providers": { 54 + "active": {"provider": "local", "model": LOCAL_MODEL}, 55 + "local": { 56 + "endpoint_url": "https://brain.example.test/v1", 57 + "served_model_id": "served-model", 58 + "credential": credential, 59 + }, 60 + }, 61 + "services": { 62 + "confidential": { 63 + "enabled_at": NOW.isoformat(), 64 + "account_id": "acct-a", 65 + "endpoint_url": "https://brain.example.test/v1", 66 + "served_model_id": "served-model", 67 + "credential_created_at": NOW.isoformat(), 68 + "credential_fingerprint_sha256": hashlib.sha256( 69 + credential.encode("utf-8") 70 + ).hexdigest(), 71 + "prior_active": { 72 + "provider": "google", 73 + "model": "gemini-flash-latest", 74 + }, 75 + "prior_local_endpoint": None, 76 + } 77 + }, 78 + "env": {}, 79 + } 80 + ), 81 + encoding="utf-8", 82 + ) 83 + 84 + 85 + def _component(now: datetime) -> dict[str, str]: 86 + return { 87 + "status": "ok", 88 + "observed_at": now.isoformat(), 89 + "expires_at": (now + DEFAULT_READY_EVIDENCE_TTL).isoformat(), 90 + } 91 + 92 + 93 + def _write_spp_ready_record(journal: Path) -> None: 94 + _write_spp_config(journal) 95 + permit = begin_brain_refresh(NOW, journal_path=journal) 96 + assert permit is not None 97 + finish_brain_refresh( 98 + permit, 99 + { 100 + "configuration": _component(NOW), 101 + "lane_prerequisites": _component(NOW), 102 + "generate": _component(NOW), 103 + "cogitate": _component(NOW), 104 + }, 105 + NOW, 106 + journal_path=journal, 107 + ) 108 + 109 + 39 110 def test_refresh_permit_excludes_contender_and_crash_releases(tmp_path: Path) -> None: 40 111 _write_config(tmp_path) 41 112 ready = tmp_path / "ready" ··· 107 178 if holder.poll() is None: 108 179 holder.terminate() 109 180 holder.wait(timeout=5) 181 + 182 + 183 + def test_prerequisite_renewal_permit_excludes_contender_and_crash_releases( 184 + tmp_path: Path, 185 + ) -> None: 186 + _write_spp_ready_record(tmp_path) 187 + ready = tmp_path / "ready" 188 + holder_code = f""" 189 + import pathlib 190 + import time 191 + from datetime import datetime 192 + from solstone.think.providers.brain_state import begin_brain_prerequisite_renewal 193 + now = datetime.fromisoformat({(NOW + timedelta(seconds=1)).isoformat()!r}) 194 + result = begin_brain_prerequisite_renewal(now, journal_path={str(tmp_path)!r}) 195 + assert result["status"] == "started", result 196 + pathlib.Path({str(ready)!r}).write_text("ready") 197 + while True: 198 + time.sleep(0.05) 199 + """ 200 + contender_code = f""" 201 + from datetime import datetime 202 + from solstone.think.providers.brain_state import begin_brain_prerequisite_renewal 203 + now = datetime.fromisoformat({(NOW + timedelta(seconds=2)).isoformat()!r}) 204 + result = begin_brain_prerequisite_renewal(now, journal_path={str(tmp_path)!r}) 205 + print(result["status"], flush=True) 206 + if result["status"] == "started": 207 + result["permit"].release() 208 + """ 209 + holder = subprocess.Popen( 210 + [sys.executable, "-c", holder_code], 211 + cwd=Path.cwd(), 212 + env=_env(tmp_path), 213 + stdout=subprocess.PIPE, 214 + stderr=subprocess.PIPE, 215 + text=True, 216 + ) 217 + try: 218 + deadline = time.monotonic() + 10 219 + while not ready.exists() and time.monotonic() < deadline: 220 + time.sleep(0.05) 221 + assert ready.exists() 222 + 223 + busy = subprocess.run( 224 + [sys.executable, "-c", contender_code], 225 + cwd=Path.cwd(), 226 + env=_env(tmp_path), 227 + capture_output=True, 228 + text=True, 229 + check=True, 230 + ) 231 + assert busy.stdout.strip() == "busy" 232 + 233 + holder.terminate() 234 + stdout, stderr = holder.communicate(timeout=10) 235 + assert holder.returncode is not None, (stdout, stderr) 236 + 237 + free = subprocess.run( 238 + [sys.executable, "-c", contender_code], 239 + cwd=Path.cwd(), 240 + env=_env(tmp_path), 241 + capture_output=True, 242 + text=True, 243 + check=True, 244 + ) 245 + assert free.stdout.strip() == "started" 246 + finally: 247 + if holder.poll() is None: 248 + holder.terminate() 249 + holder.wait(timeout=5)
+998
tests/test_cortex.py
··· 10 10 import sys 11 11 import threading 12 12 import time 13 + from datetime import datetime, timedelta, timezone 13 14 from pathlib import Path 14 15 from unittest.mock import MagicMock, patch 15 16 ··· 68 69 return journal_path / "talents" / name.replace(":", "--") / f"{use_id}.jsonl" 69 70 70 71 72 + class _FakeClock: 73 + def __init__(self, now: datetime): 74 + self.now = now 75 + self.waits: list[float] = [] 76 + 77 + def __call__(self) -> datetime: 78 + return self.now 79 + 80 + def advance(self, seconds: float) -> None: 81 + self.now += timedelta(seconds=seconds) 82 + 83 + def wait(self, seconds: float) -> bool: 84 + self.waits.append(seconds) 85 + self.advance(seconds) 86 + return False 87 + 88 + 89 + class _FakeCallosum: 90 + def __init__(self) -> None: 91 + self.emitted: list[tuple[tuple, dict]] = [] 92 + 93 + def emit(self, *args, **kwargs) -> None: 94 + self.emitted.append((args, kwargs)) 95 + 96 + 97 + def _spp_inspector(state: dict): 98 + def inspect(now: datetime, *, journal_path=None): 99 + lane = state.get("lane", "spp") 100 + aggregate = state.get("aggregate", "ready") 101 + fingerprint = state.get("fingerprint", "a" * 64) 102 + record_fingerprint = state.get("record_fingerprint", fingerprint) 103 + observed = state.get("observed", now) 104 + expires = state.get("expires", now + timedelta(minutes=10)) 105 + record = None 106 + if lane == "spp" and state.get("record_present", True): 107 + component = None 108 + if state.get("component_present", True): 109 + component = { 110 + "status": state.get("component_status", "ok"), 111 + "observed_at": observed.isoformat(), 112 + "expires_at": expires.isoformat(), 113 + } 114 + reason = state.get("component_reason") 115 + if reason is not None: 116 + component["reason_code"] = reason 117 + record = { 118 + "active_lane": "spp", 119 + "fingerprint_sha256": record_fingerprint, 120 + "evidence": {"lane_prerequisites": component}, 121 + } 122 + return { 123 + "status": "ok", 124 + "path": "/redacted", 125 + "record": record, 126 + "projection": { 127 + "aggregate_state": aggregate, 128 + "active_lane": lane, 129 + "fingerprint_sha256": fingerprint, 130 + "runtime_transition_in_progress": False, 131 + }, 132 + "reason_code": None, 133 + "error": None, 134 + } 135 + 136 + return inspect 137 + 138 + 139 + def _make_spp_controller( 140 + tmp_path: Path, 141 + monkeypatch: pytest.MonkeyPatch, 142 + *, 143 + state: dict, 144 + clock: _FakeClock | None = None, 145 + logger: MagicMock | None = None, 146 + callosum: _FakeCallosum | None = None, 147 + ): 148 + from solstone.think import cortex 149 + 150 + clock = clock or _FakeClock(datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc)) 151 + callosum = callosum or _FakeCallosum() 152 + logger = logger or MagicMock() 153 + monkeypatch.setattr(cortex, "inspect_brain_state", _spp_inspector(state)) 154 + monkeypatch.setattr( 155 + cortex, 156 + "read_active_brain_fingerprint_sha256", 157 + lambda *, journal_path=None: state.get("fingerprint", "a" * 64), 158 + ) 159 + controller = cortex.SppRenewalController( 160 + callosum=callosum, 161 + stop_event=threading.Event(), 162 + logger=logger, 163 + clock=clock, 164 + wait=clock.wait, 165 + journal_path=tmp_path, 166 + ) 167 + return controller, clock, callosum, logger 168 + 169 + 170 + def _commands(callosum: _FakeCallosum) -> list[list[str]]: 171 + return [kwargs["cmd"] for _args, kwargs in callosum.emitted] 172 + 173 + 174 + def _assert_fenced_refresh_command(command: list[str], fingerprint: str) -> None: 175 + assert command[:4] == ["journal", "brain", "refresh", "--json"] 176 + assert "--expected-active-fingerprint" in command 177 + expected_index = command.index("--expected-fingerprint") 178 + assert command[expected_index + 1] == fingerprint 179 + 180 + 181 + def _assert_absence_fenced_refresh_command(command: list[str]) -> None: 182 + assert command == [ 183 + "journal", 184 + "brain", 185 + "refresh", 186 + "--json", 187 + "--expect-active-fingerprint-absent", 188 + ] 189 + 190 + 71 191 @pytest.fixture 72 192 def mock_journal(tmp_path, monkeypatch): 73 193 """Set up a temporary journal directory.""" ··· 173 293 if cortex_service._spawn_worker is not None: 174 294 cortex_service._spawn_worker.join(timeout=1) 175 295 service_thread.join(timeout=1) 296 + 297 + 298 + def test_spp_renewal_controller_replaces_prerequisites_for_seventy_virtual_minutes( 299 + tmp_path, 300 + monkeypatch, 301 + ): 302 + from solstone.think import cortex 303 + 304 + start = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 305 + clock = _FakeClock(start) 306 + state = { 307 + "fingerprint": "a" * 64, 308 + "observed": start, 309 + "expires": start + timedelta(minutes=10), 310 + } 311 + monkeypatch.setattr(cortex, "inspect_brain_state", _spp_inspector(state)) 312 + monkeypatch.setattr( 313 + cortex, 314 + "read_active_brain_fingerprint_sha256", 315 + lambda *, journal_path=None: state["fingerprint"], 316 + ) 317 + callosum = _FakeCallosum() 318 + controller = cortex.SppRenewalController( 319 + callosum=callosum, 320 + stop_event=threading.Event(), 321 + logger=MagicMock(), 322 + clock=clock, 323 + wait=clock.wait, 324 + journal_path=tmp_path, 325 + ) 326 + 327 + committed: list[tuple[datetime, datetime]] = [] 328 + while clock.now < start + timedelta(minutes=72): 329 + delay = controller.step() 330 + if delay > 0: 331 + clock.advance(delay) 332 + assert state["expires"] > clock.now 333 + controller.step() 334 + ref = callosum.emitted[-1][1]["ref"] 335 + controller.handle_supervisor_message( 336 + {"tract": "supervisor", "event": "started", "ref": ref} 337 + ) 338 + state["observed"] = clock.now 339 + state["expires"] = clock.now + timedelta(minutes=10) 340 + committed.append((state["observed"], state["expires"])) 341 + controller.handle_supervisor_message( 342 + {"tract": "supervisor", "event": "stopped", "ref": ref, "exit_code": 0} 343 + ) 344 + 345 + commands = _commands(callosum) 346 + assert commands 347 + assert len(committed) >= 9 348 + assert clock.now >= start + timedelta(minutes=72) 349 + assert start + timedelta(minutes=72) > start + timedelta(minutes=60) 350 + assert any(observed >= start + timedelta(minutes=30) for observed, _ in committed) 351 + assert any(observed >= start + timedelta(minutes=60) for observed, _ in committed) 352 + assert all( 353 + later[0] > earlier[0] and later[1] > earlier[1] 354 + for earlier, later in zip(committed, committed[1:]) 355 + ) 356 + assert all( 357 + command[:3] == ["journal", "brain", "renew-prerequisites"] 358 + for command in commands 359 + ) 360 + assert not any( 361 + command[:3] == ["journal", "brain", "refresh"] for command in commands 362 + ) 363 + assert not any( 364 + "generate" in command or "cogitate" in command for command in commands 365 + ) 366 + 367 + 368 + def test_spp_renewal_controller_tracks_skipped_active_ref_successor( 369 + tmp_path, 370 + monkeypatch, 371 + ): 372 + from solstone.think import cortex 373 + 374 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 375 + clock = _FakeClock(now) 376 + state = { 377 + "fingerprint": "b" * 64, 378 + "observed": now - timedelta(minutes=9), 379 + "expires": now + timedelta(seconds=30), 380 + } 381 + monkeypatch.setattr(cortex, "inspect_brain_state", _spp_inspector(state)) 382 + monkeypatch.setattr( 383 + cortex, 384 + "read_active_brain_fingerprint_sha256", 385 + lambda *, journal_path=None: state["fingerprint"], 386 + ) 387 + callosum = _FakeCallosum() 388 + controller = cortex.SppRenewalController( 389 + callosum=callosum, 390 + stop_event=threading.Event(), 391 + logger=MagicMock(), 392 + clock=clock, 393 + wait=clock.wait, 394 + journal_path=tmp_path, 395 + ) 396 + 397 + controller.step() 398 + first_ref = callosum.emitted[-1][1]["ref"] 399 + controller.handle_supervisor_message( 400 + { 401 + "tract": "supervisor", 402 + "event": "skipped", 403 + "ref": first_ref, 404 + "active_ref": "active-brain", 405 + "reason": "still_running", 406 + } 407 + ) 408 + controller.step() 409 + assert len(callosum.emitted) == 1 410 + 411 + controller.handle_supervisor_message( 412 + { 413 + "tract": "supervisor", 414 + "event": "stopped", 415 + "ref": "active-brain", 416 + "exit_code": 0, 417 + } 418 + ) 419 + controller.step() 420 + assert len(callosum.emitted) == 2 421 + assert callosum.emitted[-1][1]["ref"] != first_ref 422 + 423 + 424 + def test_spp_renewal_controller_times_out_missed_successor_stop( 425 + tmp_path, 426 + monkeypatch, 427 + ): 428 + from solstone.think import cortex 429 + 430 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 431 + state = { 432 + "fingerprint": "b" * 64, 433 + "observed": now - timedelta(minutes=9), 434 + "expires": now + timedelta(seconds=30), 435 + } 436 + controller, clock, callosum, _logger = _make_spp_controller( 437 + tmp_path, 438 + monkeypatch, 439 + state=state, 440 + clock=_FakeClock(now), 441 + ) 442 + 443 + controller.step() 444 + first_ref = callosum.emitted[-1][1]["ref"] 445 + controller.handle_supervisor_message( 446 + { 447 + "tract": "supervisor", 448 + "event": "skipped", 449 + "ref": first_ref, 450 + "active_ref": "active-brain", 451 + "reason": "still_running", 452 + } 453 + ) 454 + clock.advance(cortex.SPP_REFRESH_OBSERVATION_BOUND_S + 1) 455 + 456 + controller.step() 457 + 458 + assert controller._successor_after_ref is None 459 + assert len(callosum.emitted) == 2 460 + assert callosum.emitted[-1][1]["ref"] != first_ref 461 + 462 + 463 + def test_spp_renewal_controller_accepts_full_refresh_exit_zero_without_retry( 464 + tmp_path, 465 + monkeypatch, 466 + ): 467 + from solstone.think import cortex 468 + 469 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 470 + state = { 471 + "aggregate": "unknown", 472 + "fingerprint": "e" * 64, 473 + "observed": now - timedelta(minutes=9), 474 + "expires": now + timedelta(seconds=30), 475 + } 476 + controller, clock, callosum, _logger = _make_spp_controller( 477 + tmp_path, 478 + monkeypatch, 479 + state=state, 480 + clock=_FakeClock(now), 481 + ) 482 + 483 + controller.step() 484 + _assert_fenced_refresh_command(callosum.emitted[-1][1]["cmd"], state["fingerprint"]) 485 + ref = callosum.emitted[-1][1]["ref"] 486 + controller.handle_supervisor_message( 487 + {"tract": "supervisor", "event": "started", "ref": ref} 488 + ) 489 + assert controller._running_deadline is not None 490 + assert ( 491 + controller._running_deadline - clock.now 492 + ).total_seconds() == cortex.SPP_REFRESH_OBSERVATION_BOUND_S 493 + 494 + clock.advance(1) 495 + state["aggregate"] = "ready" 496 + state["observed"] = clock.now 497 + state["expires"] = clock.now + timedelta(minutes=10) 498 + controller.handle_supervisor_message( 499 + {"tract": "supervisor", "event": "stopped", "ref": ref, "exit_code": 0} 500 + ) 501 + 502 + assert controller._retry_after is None 503 + assert controller._running_ref is None 504 + delay = controller.step() 505 + assert len(callosum.emitted) == 1 506 + assert delay > 0 507 + 508 + 509 + @pytest.mark.parametrize("case", ("unchanged", "unhealthy", "wrong_fingerprint")) 510 + def test_spp_renewal_controller_rejects_exit_zero_without_new_ready_refresh_proof( 511 + tmp_path, 512 + monkeypatch, 513 + case, 514 + ): 515 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 516 + original_fingerprint = "e" * 64 517 + state = { 518 + "aggregate": "unknown", 519 + "fingerprint": original_fingerprint, 520 + "observed": now - timedelta(minutes=9), 521 + "expires": now + timedelta(seconds=30), 522 + } 523 + controller, clock, callosum, logger = _make_spp_controller( 524 + tmp_path, 525 + monkeypatch, 526 + state=state, 527 + clock=_FakeClock(now), 528 + ) 529 + 530 + controller.step() 531 + _assert_fenced_refresh_command(callosum.emitted[-1][1]["cmd"], original_fingerprint) 532 + ref = callosum.emitted[-1][1]["ref"] 533 + controller.handle_supervisor_message( 534 + {"tract": "supervisor", "event": "started", "ref": ref} 535 + ) 536 + 537 + clock.advance(1) 538 + if case == "unchanged": 539 + state["aggregate"] = "ready" 540 + elif case == "unhealthy": 541 + state["aggregate"] = "unhealthy" 542 + state["component_status"] = "failed" 543 + state["component_reason"] = "attestation_rejected" 544 + state["observed"] = clock.now 545 + state["expires"] = clock.now + timedelta(minutes=10) 546 + else: 547 + state["aggregate"] = "ready" 548 + state["fingerprint"] = "f" * 64 549 + state["record_fingerprint"] = "f" * 64 550 + state["observed"] = clock.now 551 + state["expires"] = clock.now + timedelta(minutes=10) 552 + 553 + controller.handle_supervisor_message( 554 + {"tract": "supervisor", "event": "stopped", "ref": ref, "exit_code": 0} 555 + ) 556 + 557 + assert controller._retry_after is not None 558 + logged = "\n".join( 559 + " ".join(str(part) for part in call.args) for call in logger.info.call_args_list 560 + ) 561 + assert "verified" not in logged 562 + assert "failed" in logged 563 + 564 + 565 + def test_spp_renewal_controller_running_timeout_clears_and_retries( 566 + tmp_path, 567 + monkeypatch, 568 + ): 569 + from solstone.think import cortex 570 + 571 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 572 + state = { 573 + "fingerprint": "f" * 64, 574 + "observed": now - timedelta(minutes=9), 575 + "expires": now + timedelta(seconds=30), 576 + } 577 + controller, clock, callosum, _logger = _make_spp_controller( 578 + tmp_path, 579 + monkeypatch, 580 + state=state, 581 + clock=_FakeClock(now), 582 + ) 583 + 584 + controller.step() 585 + ref = callosum.emitted[-1][1]["ref"] 586 + controller.handle_supervisor_message( 587 + {"tract": "supervisor", "event": "started", "ref": ref} 588 + ) 589 + assert controller._running_deadline is not None 590 + assert ( 591 + controller._running_deadline - clock.now 592 + ).total_seconds() == cortex.SPP_RENEWAL_ATTEMPT_BOUND_S 593 + clock.advance(cortex.SPP_RENEWAL_ATTEMPT_BOUND_S + 1) 594 + delay = controller.step() 595 + 596 + assert controller._running_ref is None 597 + assert controller._retry_after is not None 598 + assert delay == 5.0 599 + 600 + clock.advance(delay) 601 + controller.step() 602 + assert len(callosum.emitted) == 2 603 + assert callosum.emitted[-1][1]["ref"] != ref 604 + 605 + 606 + @pytest.mark.parametrize( 607 + "state_update", 608 + ( 609 + {"record_present": False}, 610 + {"component_status": "failed", "component_reason": "attestation_not_verified"}, 611 + {"record_fingerprint": "0" * 64}, 612 + {"expires": datetime(2026, 7, 24, 11, 59, tzinfo=timezone.utc)}, 613 + ), 614 + ) 615 + def test_spp_renewal_controller_unsafe_spp_records_fall_back_to_full_refresh( 616 + tmp_path, 617 + monkeypatch, 618 + state_update, 619 + ): 620 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 621 + state = { 622 + "fingerprint": "9" * 64, 623 + "observed": now - timedelta(minutes=9), 624 + "expires": now + timedelta(seconds=30), 625 + **state_update, 626 + } 627 + _controller, _clock, callosum, _logger = _make_spp_controller( 628 + tmp_path, 629 + monkeypatch, 630 + state=state, 631 + clock=_FakeClock(now), 632 + ) 633 + 634 + _controller.step() 635 + 636 + _assert_fenced_refresh_command(callosum.emitted[-1][1]["cmd"], state["fingerprint"]) 637 + 638 + 639 + def test_spp_renewal_controller_bootstraps_with_absence_fenced_refresh( 640 + tmp_path, 641 + monkeypatch, 642 + ): 643 + from solstone.think import cortex 644 + 645 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 646 + state = { 647 + "aggregate": "unknown", 648 + "fingerprint": None, 649 + "observed": now - timedelta(minutes=9), 650 + "expires": now + timedelta(seconds=30), 651 + } 652 + controller, clock, callosum, logger = _make_spp_controller( 653 + tmp_path, 654 + monkeypatch, 655 + state=state, 656 + clock=_FakeClock(now), 657 + ) 658 + 659 + assert controller.step() == cortex.SPP_RENEWAL_ACK_TIMEOUT_S 660 + _assert_absence_fenced_refresh_command(callosum.emitted[-1][1]["cmd"]) 661 + ref = callosum.emitted[-1][1]["ref"] 662 + controller.handle_supervisor_message( 663 + {"tract": "supervisor", "event": "started", "ref": ref} 664 + ) 665 + clock.advance(1) 666 + state["aggregate"] = "ready" 667 + state["fingerprint"] = "a" * 64 668 + state["record_fingerprint"] = "a" * 64 669 + state["observed"] = clock.now 670 + state["expires"] = clock.now + timedelta(minutes=10) 671 + controller.handle_supervisor_message( 672 + {"tract": "supervisor", "event": "stopped", "ref": ref, "exit_code": 0} 673 + ) 674 + 675 + assert controller._retry_after is None 676 + logged = "\n".join(str(call.args) for call in logger.info.call_args_list) 677 + assert "verified" in logged 678 + 679 + 680 + def test_spp_renewal_controller_absence_fenced_refresh_retries_when_key_appears( 681 + tmp_path, 682 + monkeypatch, 683 + ): 684 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 685 + state = { 686 + "aggregate": "unknown", 687 + "fingerprint": None, 688 + "observed": now - timedelta(minutes=9), 689 + "expires": now + timedelta(seconds=30), 690 + } 691 + controller, clock, callosum, _logger = _make_spp_controller( 692 + tmp_path, 693 + monkeypatch, 694 + state=state, 695 + clock=_FakeClock(now), 696 + ) 697 + 698 + controller.step() 699 + _assert_absence_fenced_refresh_command(callosum.emitted[-1][1]["cmd"]) 700 + ref = callosum.emitted[-1][1]["ref"] 701 + controller.handle_supervisor_message( 702 + {"tract": "supervisor", "event": "started", "ref": ref} 703 + ) 704 + state["fingerprint"] = "b" * 64 705 + state["record_fingerprint"] = "b" * 64 706 + controller.handle_supervisor_message( 707 + {"tract": "supervisor", "event": "stopped", "ref": ref, "exit_code": 3} 708 + ) 709 + 710 + assert controller._retry_after is not None 711 + clock.advance((controller._retry_after - clock.now).total_seconds()) 712 + controller.step() 713 + _assert_fenced_refresh_command(callosum.emitted[-1][1]["cmd"], "b" * 64) 714 + 715 + 716 + def test_spp_renewal_controller_restarts_from_ready_vs_stale_record( 717 + tmp_path, 718 + monkeypatch, 719 + ): 720 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 721 + ready_state = { 722 + "fingerprint": "1" * 64, 723 + "observed": now, 724 + "expires": now + timedelta(minutes=10), 725 + } 726 + ready, _clock, ready_callosum, _logger = _make_spp_controller( 727 + tmp_path, 728 + monkeypatch, 729 + state=ready_state, 730 + clock=_FakeClock(now), 731 + ) 732 + 733 + delay = ready.step() 734 + assert ready_callosum.emitted == [] 735 + assert delay > 0 736 + 737 + stale_state = { 738 + "fingerprint": "1" * 64, 739 + "observed": now - timedelta(minutes=20), 740 + "expires": now - timedelta(seconds=1), 741 + } 742 + stale, _clock, stale_callosum, _logger = _make_spp_controller( 743 + tmp_path, 744 + monkeypatch, 745 + state=stale_state, 746 + clock=_FakeClock(now), 747 + ) 748 + 749 + stale.step() 750 + _assert_fenced_refresh_command( 751 + stale_callosum.emitted[-1][1]["cmd"], 752 + stale_state["fingerprint"], 753 + ) 754 + 755 + 756 + def test_spp_renewal_controller_pending_fingerprint_switch_is_refenced( 757 + tmp_path, 758 + monkeypatch, 759 + ): 760 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 761 + first_fingerprint = "2" * 64 762 + second_fingerprint = "3" * 64 763 + state = { 764 + "fingerprint": first_fingerprint, 765 + "observed": now - timedelta(minutes=9), 766 + "expires": now + timedelta(seconds=30), 767 + } 768 + controller, clock, callosum, _logger = _make_spp_controller( 769 + tmp_path, 770 + monkeypatch, 771 + state=state, 772 + clock=_FakeClock(now), 773 + ) 774 + 775 + controller.step() 776 + first_ref = callosum.emitted[-1][1]["ref"] 777 + assert first_fingerprint in callosum.emitted[-1][1]["cmd"] 778 + controller.handle_supervisor_message( 779 + {"tract": "supervisor", "event": "started", "ref": first_ref} 780 + ) 781 + state["fingerprint"] = second_fingerprint 782 + state["record_fingerprint"] = second_fingerprint 783 + state["observed"] = clock.now 784 + state["expires"] = clock.now + timedelta(seconds=30) 785 + controller.handle_supervisor_message( 786 + {"tract": "supervisor", "event": "stopped", "ref": first_ref, "exit_code": 3} 787 + ) 788 + assert controller._retry_after is not None 789 + 790 + clock.advance((controller._retry_after - clock.now).total_seconds()) 791 + controller.step() 792 + 793 + assert len(callosum.emitted) == 2 794 + assert second_fingerprint in callosum.emitted[-1][1]["cmd"] 795 + 796 + 797 + def test_spp_renewal_controller_refresh_fallback_is_refenced_after_switch( 798 + tmp_path, 799 + monkeypatch, 800 + ): 801 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 802 + first_fingerprint = "2" * 64 803 + second_fingerprint = "3" * 64 804 + state = { 805 + "aggregate": "unknown", 806 + "fingerprint": first_fingerprint, 807 + "observed": now - timedelta(minutes=9), 808 + "expires": now + timedelta(seconds=30), 809 + } 810 + controller, clock, callosum, _logger = _make_spp_controller( 811 + tmp_path, 812 + monkeypatch, 813 + state=state, 814 + clock=_FakeClock(now), 815 + ) 816 + 817 + controller.step() 818 + first_cmd = callosum.emitted[-1][1]["cmd"] 819 + _assert_fenced_refresh_command(first_cmd, first_fingerprint) 820 + first_ref = callosum.emitted[-1][1]["ref"] 821 + controller.handle_supervisor_message( 822 + {"tract": "supervisor", "event": "started", "ref": first_ref} 823 + ) 824 + state["fingerprint"] = second_fingerprint 825 + state["record_fingerprint"] = second_fingerprint 826 + state["observed"] = clock.now 827 + state["expires"] = clock.now + timedelta(seconds=30) 828 + controller.handle_supervisor_message( 829 + {"tract": "supervisor", "event": "stopped", "ref": first_ref, "exit_code": 3} 830 + ) 831 + assert controller._retry_after is not None 832 + 833 + clock.advance((controller._retry_after - clock.now).total_seconds()) 834 + controller.step() 835 + 836 + assert len(callosum.emitted) == 2 837 + _assert_fenced_refresh_command(callosum.emitted[-1][1]["cmd"], second_fingerprint) 838 + 839 + 840 + def test_spp_renewal_controller_clock_jumps_do_not_duplicate( 841 + tmp_path, 842 + monkeypatch, 843 + ): 844 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 845 + state = { 846 + "fingerprint": "4" * 64, 847 + "observed": now, 848 + "expires": now + timedelta(minutes=10), 849 + } 850 + controller, clock, callosum, _logger = _make_spp_controller( 851 + tmp_path, 852 + monkeypatch, 853 + state=state, 854 + clock=_FakeClock(now), 855 + ) 856 + 857 + delay = controller.step() 858 + assert delay >= 0 859 + assert callosum.emitted == [] 860 + 861 + clock.advance(delay + 1) 862 + controller.step() 863 + assert callosum.emitted[-1][1]["cmd"][:3] == [ 864 + "journal", 865 + "brain", 866 + "renew-prerequisites", 867 + ] 868 + 869 + backward_state = { 870 + "fingerprint": "5" * 64, 871 + "observed": now, 872 + "expires": now + timedelta(minutes=10), 873 + } 874 + backward, backward_clock, backward_callosum, _logger = _make_spp_controller( 875 + tmp_path, 876 + monkeypatch, 877 + state=backward_state, 878 + clock=_FakeClock(now), 879 + ) 880 + backward_delay = backward.step() 881 + backward_clock.advance(-600) 882 + later_delay = backward.step() 883 + 884 + assert backward_delay >= 0 885 + assert later_delay >= 0 886 + assert backward_callosum.emitted == [] 887 + 888 + 889 + def test_spp_renewal_controller_recovers_from_inspection_and_fingerprint_errors( 890 + tmp_path, 891 + monkeypatch, 892 + ): 893 + from solstone.think import cortex 894 + 895 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 896 + state = { 897 + "fingerprint": "6" * 64, 898 + "observed": now - timedelta(minutes=9), 899 + "expires": now + timedelta(seconds=30), 900 + } 901 + clock = _FakeClock(now) 902 + callosum = _FakeCallosum() 903 + logger = MagicMock() 904 + inspect_calls = 0 905 + 906 + def flaky_inspect(current: datetime, *, journal_path=None): 907 + nonlocal inspect_calls 908 + inspect_calls += 1 909 + if inspect_calls == 1: 910 + raise OSError("SECRET-SENTINEL path") 911 + return _spp_inspector(state)(current, journal_path=journal_path) 912 + 913 + monkeypatch.setattr(cortex, "inspect_brain_state", flaky_inspect) 914 + monkeypatch.setattr( 915 + cortex, 916 + "read_active_brain_fingerprint_sha256", 917 + lambda *, journal_path=None: state["fingerprint"], 918 + ) 919 + controller = cortex.SppRenewalController( 920 + callosum=callosum, 921 + stop_event=threading.Event(), 922 + logger=logger, 923 + clock=clock, 924 + wait=clock.wait, 925 + journal_path=tmp_path, 926 + ) 927 + 928 + assert controller.step() == 5.0 929 + clock.advance(5.0) 930 + controller.step() 931 + assert callosum.emitted[-1][1]["cmd"][:3] == [ 932 + "journal", 933 + "brain", 934 + "renew-prerequisites", 935 + ] 936 + 937 + fingerprint_calls = 0 938 + 939 + def flaky_fingerprint(*, journal_path=None): 940 + nonlocal fingerprint_calls 941 + fingerprint_calls += 1 942 + if fingerprint_calls == 1: 943 + raise OSError("SECRET-SENTINEL path") 944 + return state["fingerprint"] 945 + 946 + callosum = _FakeCallosum() 947 + logger = MagicMock() 948 + monkeypatch.setattr(cortex, "inspect_brain_state", _spp_inspector(state)) 949 + monkeypatch.setattr( 950 + cortex, "read_active_brain_fingerprint_sha256", flaky_fingerprint 951 + ) 952 + controller = cortex.SppRenewalController( 953 + callosum=callosum, 954 + stop_event=threading.Event(), 955 + logger=logger, 956 + clock=clock, 957 + wait=clock.wait, 958 + journal_path=tmp_path, 959 + ) 960 + 961 + assert controller.step() == 5.0 962 + clock.advance(5.0) 963 + controller.step() 964 + assert callosum.emitted[-1][1]["cmd"][:3] == [ 965 + "journal", 966 + "brain", 967 + "renew-prerequisites", 968 + ] 969 + 970 + logged = "\n".join( 971 + " ".join(str(part) for part in call.args) for call in logger.info.call_args_list 972 + ) 973 + assert "SECRET-SENTINEL" not in logged 974 + assert str(tmp_path) not in logged 975 + 976 + 977 + def test_spp_renewal_controller_run_contains_unexpected_step_exception( 978 + tmp_path, 979 + monkeypatch, 980 + ): 981 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 982 + state = { 983 + "fingerprint": "8" * 64, 984 + "observed": now, 985 + "expires": now + timedelta(minutes=10), 986 + } 987 + controller, _clock, _callosum, logger = _make_spp_controller( 988 + tmp_path, 989 + monkeypatch, 990 + state=state, 991 + clock=_FakeClock(now), 992 + ) 993 + controller.step = MagicMock(side_effect=RuntimeError("SECRET-SENTINEL")) 994 + 995 + def stop_after_wait(_seconds: float) -> bool: 996 + controller.stop_event.set() 997 + return True 998 + 999 + controller.wait = stop_after_wait 1000 + controller.run() 1001 + 1002 + logged = "\n".join( 1003 + " ".join(str(part) for part in call.args) for call in logger.info.call_args_list 1004 + ) 1005 + assert "RuntimeError" in logged 1006 + assert "retrying" in logged 1007 + assert "SECRET-SENTINEL" not in logged 1008 + 1009 + 1010 + def test_spp_renewal_controller_retries_with_cap_and_clears_on_disable( 1011 + tmp_path, 1012 + monkeypatch, 1013 + ): 1014 + from solstone.think import cortex 1015 + 1016 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 1017 + clock = _FakeClock(now) 1018 + state = { 1019 + "fingerprint": "c" * 64, 1020 + "observed": now - timedelta(minutes=9), 1021 + "expires": now + timedelta(seconds=30), 1022 + } 1023 + monkeypatch.setattr(cortex, "inspect_brain_state", _spp_inspector(state)) 1024 + monkeypatch.setattr( 1025 + cortex, 1026 + "read_active_brain_fingerprint_sha256", 1027 + lambda *, journal_path=None: state["fingerprint"], 1028 + ) 1029 + controller = cortex.SppRenewalController( 1030 + callosum=_FakeCallosum(), 1031 + stop_event=threading.Event(), 1032 + logger=MagicMock(), 1033 + clock=clock, 1034 + wait=clock.wait, 1035 + journal_path=tmp_path, 1036 + ) 1037 + 1038 + delays: list[float] = [] 1039 + for _ in range(6): 1040 + controller.step() 1041 + clock.advance(cortex.SPP_RENEWAL_ACK_TIMEOUT_S + 1) 1042 + controller.step() 1043 + assert controller._retry_after is not None 1044 + delays.append((controller._retry_after - clock.now).total_seconds()) 1045 + clock.advance(delays[-1]) 1046 + assert delays == [5.0, 10.0, 20.0, 40.0, 60.0, 60.0] 1047 + 1048 + controller.step() 1049 + assert controller._pending_ref is not None 1050 + state["lane"] = "byo-cloud" 1051 + controller.step() 1052 + assert controller._pending_ref is None 1053 + assert controller._retry_after is None 1054 + 1055 + 1056 + def test_spp_renewal_controller_logs_lifecycle_events_without_secrets( 1057 + tmp_path, 1058 + monkeypatch, 1059 + ): 1060 + from solstone.think import cortex 1061 + 1062 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 1063 + clock = _FakeClock(now) 1064 + state = { 1065 + "lane": "byo-cloud", 1066 + "fingerprint": "d" * 64, 1067 + "observed": now - timedelta(minutes=9), 1068 + "expires": now + timedelta(seconds=30), 1069 + "secret": "SECRET-SENTINEL", 1070 + } 1071 + monkeypatch.setattr(cortex, "inspect_brain_state", _spp_inspector(state)) 1072 + monkeypatch.setattr( 1073 + cortex, 1074 + "read_active_brain_fingerprint_sha256", 1075 + lambda *, journal_path=None: state["fingerprint"], 1076 + ) 1077 + logger = MagicMock() 1078 + callosum = _FakeCallosum() 1079 + controller = cortex.SppRenewalController( 1080 + callosum=callosum, 1081 + stop_event=threading.Event(), 1082 + logger=logger, 1083 + clock=clock, 1084 + wait=clock.wait, 1085 + journal_path=tmp_path, 1086 + ) 1087 + 1088 + assert controller.step() == 30.0 1089 + assert callosum.emitted == [] 1090 + 1091 + state["lane"] = "spp" 1092 + state["expires"] = clock.now + timedelta(minutes=10) 1093 + scheduled_delay = controller.step() 1094 + assert scheduled_delay > 0 1095 + clock.advance(scheduled_delay) 1096 + controller.step() 1097 + assert callosum.emitted[-1][1]["cmd"][:3] == [ 1098 + "journal", 1099 + "brain", 1100 + "renew-prerequisites", 1101 + ] 1102 + verified_ref = callosum.emitted[-1][1]["ref"] 1103 + controller.handle_supervisor_message( 1104 + {"tract": "supervisor", "event": "started", "ref": verified_ref} 1105 + ) 1106 + clock.advance(1) 1107 + state["observed"] = clock.now 1108 + state["expires"] = clock.now + timedelta(minutes=10) 1109 + controller.handle_supervisor_message( 1110 + { 1111 + "tract": "supervisor", 1112 + "event": "stopped", 1113 + "ref": verified_ref, 1114 + "exit_code": 0, 1115 + } 1116 + ) 1117 + 1118 + state["expires"] = clock.now + timedelta(seconds=30) 1119 + controller.step() 1120 + failed_ref = callosum.emitted[-1][1]["ref"] 1121 + clock.advance(cortex.SPP_RENEWAL_ACK_TIMEOUT_S + 1) 1122 + controller.step() 1123 + clock.advance(5.0) 1124 + controller.step() 1125 + stale_ref = callosum.emitted[-1][1]["ref"] 1126 + assert stale_ref != failed_ref 1127 + controller.handle_supervisor_message( 1128 + {"tract": "supervisor", "event": "started", "ref": stale_ref} 1129 + ) 1130 + clock.advance(cortex.SPP_RENEWAL_ATTEMPT_BOUND_S + 1) 1131 + controller.step() 1132 + 1133 + logged = "\n".join( 1134 + " ".join(str(part) for part in call.args) for call in logger.info.call_args_list 1135 + ) 1136 + for event in ( 1137 + "disabled", 1138 + "scheduled", 1139 + "in_flight", 1140 + "verified", 1141 + "failed", 1142 + "stale", 1143 + "retrying", 1144 + ): 1145 + assert event in logged 1146 + assert "SECRET-SENTINEL" not in logged 1147 + assert str(tmp_path) not in logged 1148 + assert "SECRET-SENTINEL" not in json.dumps(callosum.emitted) 1149 + 1150 + 1151 + def test_cortex_stop_wakes_and_joins_spp_renewal_worker( 1152 + mock_journal, 1153 + monkeypatch, 1154 + ): 1155 + from solstone.think import cortex 1156 + from solstone.think.cortex import CortexService 1157 + 1158 + now = datetime(2026, 7, 24, 12, 0, tzinfo=timezone.utc) 1159 + state = {"lane": "byo-cloud", "fingerprint": "7" * 64} 1160 + monkeypatch.setattr(cortex, "inspect_brain_state", _spp_inspector(state)) 1161 + service = CortexService(str(mock_journal), clock=lambda: now) 1162 + service.callosum = MagicMock() 1163 + 1164 + service._start_spp_renewal_controller() 1165 + assert service._spp_renewal_worker is not None 1166 + deadline = time.monotonic() + 1.0 1167 + while not service._spp_renewal_worker.is_alive() and time.monotonic() < deadline: 1168 + time.sleep(0.01) 1169 + assert service._spp_renewal_worker.is_alive() 1170 + 1171 + service.stop() 1172 + 1173 + assert not service._spp_renewal_worker.is_alive() 176 1174 177 1175 178 1176 def test_handle_request_dedups_existing_active_file(
+8 -2
tests/test_no_implicit_cloud.py
··· 823 823 establish.assert_called_once() 824 824 825 825 826 - def test_confidential_stt_stale_session_defers_before_egress(tmp_path, monkeypatch): 826 + def test_confidential_stt_stale_session_replacement_failure_defers_before_egress( 827 + tmp_path, monkeypatch 828 + ): 827 829 from solstone.think.services import spp, spp_transport 830 + from solstone.think.services.spp_attest.ratls.channel import RatlsChannelError 828 831 829 832 _empty_journal(tmp_path, monkeypatch) 830 833 config = _confidential_config(provider_pins=False) ··· 835 838 spp_transport._FORWARDER_BASE_URL = "http://127.0.0.1:4567" 836 839 spp.record_attestation_verified(_stale_session(object())) 837 840 mocks = _install_stt_backend_mocks(monkeypatch, confidential_result=None) 841 + establish = Mock(side_effect=RatlsChannelError("gateway_unreachable")) 842 + monkeypatch.setattr(spp_transport, "establish_attested_channel", establish) 838 843 httpx_post = Mock(side_effect=AssertionError("audio egress attempted")) 839 844 monkeypatch.setattr("httpx.post", httpx_post) 840 845 ··· 843 848 with pytest.raises(ConfidentialTranscribeDeferral) as exc_info: 844 849 transcribe("confidential", _stt_audio(), 16000, {}) 845 850 846 - assert exc_info.value.reason_code == "attestation_stale" 851 + assert exc_info.value.reason_code == "attestation_unreachable" 847 852 mocks["parakeet"].assert_not_called() 848 853 httpx_post.assert_not_called() 854 + establish.assert_called_once() 849 855 850 856 851 857 def test_confidential_stt_setting_off_gate_blocks_confidential_only(
+1
tests/test_supervisor.py
··· 588 588 assert get(["journal", "indexer", "--rescan"]) == "indexer" 589 589 assert get(["sol", "insight", "20240101"]) == "insight" 590 590 assert get(["journal", "think", "--day", "20240101"]) == "daily" 591 + assert get(["journal", "brain", "renew-prerequisites"]) == "brain" 591 592 assert get(["journal", "maintenance", "list"]) == "maintenance" 592 593 assert get(["journal", "maintenance", "run", "foo:bar"]) == "maintenance:foo:bar" 593 594 assert get(["journal", "maintenance", "run", "baz:qux"]) == "maintenance:baz:qux"