Skip to main content

saddle_observability/
diagnostic.rs

1//! Independent emergency output. Ordinary submission never performs filesystem IO;
2//! a confirmed source may synchronously take over after the worker exits.
3use crate::FileLoggingConfig;
4use saddle_core::{CallContext, Diagnostic};
5use serde::Serialize;
6use std::{
7    fmt,
8    fs::{self, File, OpenOptions},
9    io::{self, Write},
10    path::PathBuf,
11    sync::{
12        Arc, Condvar, Mutex, OnceLock,
13        atomic::{AtomicBool, AtomicU64, Ordering},
14        mpsc::{self, SyncSender, TrySendError},
15    },
16    thread::{self, JoinHandle},
17    time::Duration,
18};
19
20pub const EMERGENCY_FILE_NAME: &str = "saddle.emergency.log";
21pub const BOOTSTRAP_FILE_NAME: &str = "saddle.bootstrap.log";
22const QUEUE_CAPACITY: usize = 64;
23const RECORD_BYTES: usize = 32 * 1024;
24const TARGET_PATH_BYTES: usize = 4160;
25static ACTIVE: AtomicBool = AtomicBool::new(false);
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
28#[serde(rename_all = "snake_case")]
29pub enum DiagnosticSubmission {
30    /// No configured output exists. A light receipt retains association/status,
31    /// not source detail; legacy carriers may separately retain their detail.
32    OutputUnavailable,
33    Enqueued,
34    /// Every segment of this source, including its terminal marker, was written.
35    Written,
36    Full,
37    Closed,
38    EncodingFailed,
39}
40#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
41#[serde(rename_all = "snake_case")]
42pub enum DiagnosticShutdown {
43    Pending,
44    Finished,
45    WorkerPanicked,
46}
47#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
48pub struct DiagnosticOutputSnapshot {
49    pub initialized: bool,
50    pub first_failure: Option<DiagnosticIoFailure>,
51    pub enqueued: u64,
52    pub written: u64,
53    pub dropped: u64,
54    pub output_failed: u64,
55    pub stderr_failed: u64,
56    pub stderr_suppressed: u64,
57    pub truncated: u64,
58    pub closed: bool,
59}
60#[derive(Default)]
61struct Status {
62    owns_active: AtomicBool,
63    initialized: AtomicBool,
64    failure: AtomicU64,
65    enqueued: AtomicU64,
66    written: AtomicU64,
67    dropped: AtomicU64,
68    output_failed: AtomicU64,
69    stderr_failed: AtomicU64,
70    stderr_suppressed: AtomicU64,
71    truncated: AtomicU64,
72    closed: AtomicBool,
73    submitting: AtomicU64,
74    worker_stopped: AtomicBool,
75    fallback_path: OnceLock<BoundedTargetPath>,
76    fallback_file: Mutex<Option<File>>,
77    confirmation: Mutex<Confirmation>,
78    confirmation_ready: Condvar,
79}
80impl Drop for Status {
81    fn drop(&mut self) {
82        if self.owns_active.load(Ordering::Acquire) {
83            drop(self.fallback_file.get_mut().unwrap_or_else(|e| e.into_inner()).take());
84            ACTIVE.store(false, Ordering::Release);
85        }
86    }
87}
88#[derive(Default)]
89struct Confirmation {
90    next: u64,
91    active: Option<u64>,
92    completed: Option<(u64, io::Result<()>)>,
93}
94struct BoundedTargetPath {
95    bytes: [u8; TARGET_PATH_BYTES],
96    len: usize,
97}
98impl BoundedTargetPath {
99    fn new(path: &std::path::Path) -> io::Result<Self> {
100        use std::os::unix::ffi::OsStrExt;
101        let source = path.as_os_str().as_bytes();
102        if source.len() > TARGET_PATH_BYTES {
103            return Err(io::Error::new(io::ErrorKind::InvalidInput, "emergency path exceeds bound"));
104        }
105        let mut bytes = [0; TARGET_PATH_BYTES];
106        bytes[..source.len()].copy_from_slice(source);
107        Ok(Self { bytes, len: source.len() })
108    }
109    fn as_path(&self) -> &std::path::Path {
110        use std::os::unix::ffi::OsStrExt;
111        std::path::Path::new(std::ffi::OsStr::from_bytes(&self.bytes[..self.len]))
112    }
113}
114struct ConfirmationLease<'a> {
115    status: &'a Status,
116    sequence: u64,
117}
118impl Status {
119    fn acquire_confirmation(&self) -> ConfirmationLease<'_> {
120        let mut state = self.confirmation.lock().unwrap_or_else(|e| e.into_inner());
121        while state.active.is_some() {
122            state = self.confirmation_ready.wait(state).unwrap_or_else(|e| e.into_inner());
123        }
124        state.next = state.next.wrapping_add(1);
125        let sequence = state.next;
126        state.active = Some(sequence);
127        ConfirmationLease { status: self, sequence }
128    }
129    fn acknowledge(&self, sequence: u64, delivery: io::Result<()>) {
130        let mut state = self.confirmation.lock().unwrap_or_else(|e| e.into_inner());
131        if state.active == Some(sequence) {
132            state.completed = Some((sequence, delivery));
133            self.confirmation_ready.notify_all();
134        }
135    }
136}
137impl ConfirmationLease<'_> {
138    fn wait(&self) -> io::Result<()> {
139        let mut state = self.status.confirmation.lock().unwrap_or_else(|e| e.into_inner());
140        loop {
141            if let Some((sequence, delivery)) = state.completed.take() {
142                debug_assert_eq!(sequence, self.sequence);
143                return delivery;
144            }
145            if self.status.worker_stopped.load(Ordering::Acquire) {
146                return Err(io::Error::new(io::ErrorKind::BrokenPipe, "diagnostic writer stopped"));
147            }
148            state = self.status.confirmation_ready.wait(state).unwrap_or_else(|e| e.into_inner());
149        }
150    }
151}
152impl Drop for ConfirmationLease<'_> {
153    fn drop(&mut self) {
154        let mut state = self.status.confirmation.lock().unwrap_or_else(|e| e.into_inner());
155        if state.active == Some(self.sequence) {
156            state.active = None;
157            state.completed = None;
158            self.status.confirmation_ready.notify_all();
159        }
160    }
161}
162impl Status {
163    fn fail(&self, stage: u64, error: &io::Error) {
164        let kind = match error.kind() {
165            io::ErrorKind::PermissionDenied => 1,
166            io::ErrorKind::NotFound => 2,
167            io::ErrorKind::NotADirectory => 3,
168            io::ErrorKind::InvalidInput => 4,
169            io::ErrorKind::StorageFull => 5,
170            io::ErrorKind::BrokenPipe => 6,
171            io::ErrorKind::WouldBlock => 7,
172            _ => 8,
173        };
174        let os = error.raw_os_error();
175        let packed = (stage << 56)
176            | (kind << 48)
177            | (u64::from(os.is_some()) << 40)
178            | u64::from(os.unwrap_or(0) as u32);
179        let _ = self
180            .failure
181            .compare_exchange(0, packed, Ordering::AcqRel, Ordering::Acquire);
182    }
183    fn snapshot(&self) -> DiagnosticOutputSnapshot {
184        let failure = self.failure.load(Ordering::Acquire);
185        DiagnosticOutputSnapshot {
186            initialized: self.initialized.load(Ordering::Acquire),
187            first_failure: (failure != 0).then(|| DiagnosticIoFailure {
188                stage: if failure >> 56 == 1 {
189                    "initialize"
190                } else {
191                    "write"
192                },
193                kind: [
194                    "none",
195                    "permission_denied",
196                    "not_found",
197                    "not_a_directory",
198                    "invalid_input",
199                    "storage_full",
200                    "broken_pipe",
201                    "would_block",
202                    "other",
203                ][((failure >> 48) & 255) as usize],
204                os_code: (failure & (1 << 40) != 0).then_some(failure as u32 as i32),
205            }),
206            enqueued: self.enqueued.load(Ordering::Acquire),
207            written: self.written.load(Ordering::Acquire),
208            dropped: self.dropped.load(Ordering::Acquire),
209            output_failed: self.output_failed.load(Ordering::Acquire),
210            stderr_failed: self.stderr_failed.load(Ordering::Acquire),
211            stderr_suppressed: self.stderr_suppressed.load(Ordering::Acquire),
212            truncated: self.truncated.load(Ordering::Acquire),
213            closed: self.closed.load(Ordering::Acquire),
214        }
215    }
216}
217
218#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
219pub struct DiagnosticIoFailure {
220    pub stage: &'static str,
221    pub kind: &'static str,
222    pub os_code: Option<i32>,
223}
224
225/// Startup owner: at most one independent writer per process. No join on Drop.
226pub struct EmergencyDiagnostics {
227    handle: EmergencyDiagnosticHandle,
228    worker: Option<JoinHandle<()>>,
229    target: PathBuf,
230    source_target_preopened: bool,
231}
232/// The selected runtime original output was opened before the application
233/// could begin. Its private field prevents constructing a formal output from
234/// an arbitrary queue handle; individual writes still require confirmation.
235///
236/// ```compile_fail
237/// use saddle_observability::{EmergencyDiagnosticHandle, SourceOutput};
238/// fn forge(handle: EmergencyDiagnosticHandle) -> SourceOutput { SourceOutput(handle) }
239/// ```
240#[derive(Clone)]
241pub struct SourceOutput(EmergencyDiagnosticHandle);
242impl SourceOutput {
243    pub fn handle(&self) -> &EmergencyDiagnosticHandle { &self.0 }
244}
245/// Synchronous, configuration-independent startup original owner. It uses
246/// the same bounded record encoding and verified append open as the runtime
247/// writer, without claiming the process writer slot or spawning a thread.
248#[derive(Clone)]
249pub struct BootstrapDiagnostics {
250    inner: Arc<BootstrapInner>,
251}
252struct BootstrapInner {
253    file: Mutex<File>,
254    target: PathBuf,
255}
256static BOOTSTRAP_ACTIVE: OnceLock<Mutex<std::sync::Weak<BootstrapInner>>> = OnceLock::new();
257
258impl BootstrapDiagnostics {
259    pub fn start(directory: &std::path::Path) -> Result<Self, EmergencyInitError> {
260        validate_emergency_directory(directory)?;
261        let target = directory.join(BOOTSTRAP_FILE_NAME);
262        let registry = BOOTSTRAP_ACTIVE.get_or_init(|| Mutex::new(std::sync::Weak::new()));
263        let mut active = registry.lock().unwrap_or_else(|error| error.into_inner());
264        if let Some(inner) = active.upgrade() {
265            return if inner.target == target {
266                Ok(Self { inner })
267            } else {
268                Err(EmergencyInitError::AlreadyActive)
269            };
270        }
271        let file = open_emergency_file(&target, true)
272            .map_err(|error| EmergencyInitError::Spawn(error.kind()))?;
273        let inner = Arc::new(BootstrapInner { file: Mutex::new(file), target });
274        *active = Arc::downgrade(&inner);
275        Ok(Self { inner })
276    }
277
278    pub fn target(&self) -> &std::path::Path { &self.inner.target }
279
280    pub(crate) fn write_original_record<T: Serialize>(&self, record: &T) -> io::Result<()> {
281        let mut bytes = FixedDiagnosticBytes { bytes: [0; 8192], len: 0 };
282        serde_json::to_writer(&mut bytes, record).map_err(io::Error::other)?;
283        bytes.bytes[bytes.len] = b'\n';
284        bytes.len += 1;
285        self.inner.file.lock().unwrap_or_else(|error| error.into_inner())
286            .write_all(&bytes.bytes[..bytes.len])
287    }
288}
289#[derive(Clone)]
290pub struct EmergencyDiagnosticHandle {
291    sender: SyncSender<DiagnosticPacket>,
292    status: Arc<Status>,
293}
294
295/// Startup-instance discriminator shared by finalization records in this
296/// process. It is observational and never participates in request deadlines.
297pub(crate) fn finalization_process_instance() -> u128 {
298    static INSTANCE: std::sync::OnceLock<u128> = std::sync::OnceLock::new();
299    *INSTANCE.get_or_init(|| std::time::SystemTime::now()
300        .duration_since(std::time::UNIX_EPOCH).map_or(0, |v| v.as_nanos()))
301}
302struct DetachedObjectLifetime {
303    context: saddle_core::request_context::RequestContextSnapshot,
304    output: EmergencyDiagnosticHandle,
305    process_instance: u128,
306    kind: &'static str,
307}
308impl DetachedObjectLifetime {
309    fn emit(&self, fact: saddle_admission::ObjectLifetimeFact, created: bool) {
310        static SEQUENCE: AtomicU64 = AtomicU64::new(1);
311        static CLOCK: OnceLock<std::time::Instant> = OnceLock::new();
312        #[derive(Serialize)]
313        struct Record<'a> {
314            schema: &'static str, pid: u32, process_instance: u128, local_request: u64,
315            sequence: u64, monotonic_ns: u128, kind: &'static str, phase: &'static str,
316            object_id: u64, ledger_instance: u64, account: saddle_admission::AccountIdentity,
317            storage_bytes: usize, storage_allocated: bool, ledger_charge_applied: bool,
318            storage_destroyed: bool, ledger_charge_released: bool,
319            context: &'a saddle_core::request_context::RequestContextSnapshot,
320        }
321        let _ = self.output.submit_fixed_record(&Record {
322            schema: "request-object-lifetime/1", pid: std::process::id(),
323            process_instance: self.process_instance, local_request: self.context.local_request_id(),
324            sequence: SEQUENCE.fetch_add(1, Ordering::Relaxed),
325            monotonic_ns: CLOCK.get_or_init(std::time::Instant::now).elapsed().as_nanos().saturating_add(1),
326            kind: self.kind, phase: if created { "created" } else { "released" },
327            object_id: fact.object_id, ledger_instance: fact.ledger_instance, account: fact.account,
328            storage_bytes: fact.storage_bytes, storage_allocated: created, ledger_charge_applied: created,
329            storage_destroyed: !created, ledger_charge_released: !created, context: &self.context,
330        });
331    }
332}
333impl saddle_admission::ObjectLifetimeObserver for DetachedObjectLifetime {
334    fn created(&self, fact: saddle_admission::ObjectLifetimeFact) { self.emit(fact, true); }
335    fn released(&self, fact: saddle_admission::ObjectLifetimeFact) { self.emit(fact, false); }
336}
337fn rpc_lifecycle_stamp() -> (u64, u128) {
338    static SEQUENCE: AtomicU64 = AtomicU64::new(1);
339    static CLOCK: OnceLock<std::time::Instant> = OnceLock::new();
340    (SEQUENCE.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |n| n.checked_add(1)).unwrap_or(0),
341        CLOCK.get_or_init(std::time::Instant::now).elapsed().as_nanos().saturating_add(1))
342}
343/// Inline detached metadata; the actual enclosing Shared layout prepays it.
344#[doc(hidden)]
345#[derive(Clone)]
346pub struct RpcPhysicalOwnerReceipt {
347    context: saddle_core::request_context::RequestContextSnapshot,
348    output: EmergencyDiagnosticHandle,
349    process_instance: u128,
350    call_id: [u8; 256],
351    call_id_len: usize,
352}
353impl RpcPhysicalOwnerReceipt {
354    #[doc(hidden)]
355    pub fn driver_phase(&self, stage: saddle_admission::RpcStageReleaseFact,
356        driver_id: u64, phase: &'static str) {
357        #[derive(Serialize)]
358        struct Record<'a> {
359            schema: &'static str, event: &'static str, pid: u32, process_instance: u128,
360            local_request: u64, call_id: &'a str, driver_id: u64, phase: &'static str,
361            sequence: u64, monotonic_ns: u128, stage: saddle_admission::RpcStageReleaseFact,
362            context: &'a saddle_core::request_context::RequestContextSnapshot,
363        }
364        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
365        let _ = self.output.submit_fixed_record(&Record {
366            schema: "request-rpc-driver/1", event: "rpc_driver_lifetime", pid: std::process::id(),
367            process_instance: self.process_instance, local_request: self.context.local_request_id(),
368            call_id: std::str::from_utf8(&self.call_id[..self.call_id_len]).unwrap(), driver_id, phase,
369            sequence, monotonic_ns, stage, context: &self.context,
370        });
371    }
372    #[doc(hidden)]
373    pub fn released(self, stage: saddle_admission::RpcStageReleaseFact) {
374        #[derive(Serialize)]
375        struct Record<'a> {
376            schema: &'static str, event: &'static str, pid: u32,
377            process_instance: u128, local_request: u64, call_id: &'a str,
378            sequence: u64, monotonic_ns: u128,
379            stage: saddle_admission::RpcStageReleaseFact,
380            context: &'a saddle_core::request_context::RequestContextSnapshot,
381        }
382        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
383        let _ = self.output.submit_fixed_record(&Record {
384            schema: "request-rpc-physical-owner/1", event: "rpc_shared_allocation_released",
385            pid: std::process::id(), process_instance: self.process_instance,
386            local_request: self.context.local_request_id(),
387            call_id: std::str::from_utf8(&self.call_id[..self.call_id_len]).unwrap(),
388            sequence, monotonic_ns, stage, context: &self.context,
389        });
390    }
391}
392struct DetachedRpcStageRelease {
393    context: saddle_core::request_context::RequestContextSnapshot,
394    output: EmergencyDiagnosticHandle,
395    process_instance: u128,
396    call_id: [u8; 256],
397    call_id_len: usize,
398}
399impl saddle_admission::RpcStageReleaseObserver for DetachedRpcStageRelease {
400    fn released(&self, fact: saddle_admission::RpcStageReleaseFact) {
401        #[derive(Serialize)]
402        struct Record<'a> {
403            schema: &'static str, event: &'static str, pid: u32,
404            process_instance: u128, local_request: u64, call_id: &'a str,
405            sequence: u64, monotonic_ns: u128,
406            stage: saddle_admission::RpcStageReleaseFact,
407            context: &'a saddle_core::request_context::RequestContextSnapshot,
408        }
409        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
410        let call_id = std::str::from_utf8(&self.call_id[..self.call_id_len]).unwrap();
411        let _ = self.output.submit_fixed_record(&Record {
412            schema: "request-rpc-stage/1", event: "rpc_stage_credit_released",
413            pid: std::process::id(), process_instance: self.process_instance,
414            local_request: self.context.local_request_id(), call_id,
415            sequence, monotonic_ns,
416            stage: fact, context: &self.context,
417        });
418    }
419}
420struct DetachedAccountRelease {
421    context: saddle_core::request_context::RequestContextSnapshot,
422    output: EmergencyDiagnosticHandle,
423    process_instance: u128,
424}
425impl saddle_admission::AccountReleaseObserver for DetachedAccountRelease {
426    fn released(&self, fact: saddle_admission::AccountReleaseFact) {
427        #[derive(Serialize)]
428        struct Record<'a> {
429            schema: &'static str, event: &'static str, process_id: u32,
430            process_instance: u128, completion: &'static str,
431            account: saddle_admission::AccountReleaseFact,
432            context: &'a saddle_core::request_context::RequestContextSnapshot,
433        }
434        let _ = self.output.submit_fixed_record(&Record {
435            schema: "request-finalization/1", event: "request_account_released",
436            process_id: std::process::id(), process_instance: self.process_instance,
437            completion: "physically_released", account: fact, context: &self.context,
438        });
439    }
440}
441impl EmergencyDiagnosticHandle {
442    #[doc(hidden)]
443    pub fn record_request_stop_decision(&self,
444        context: saddle_core::request_context::RequestContextSnapshot, cause: &'static str) {
445        if !matches!(cause, "original_deadline" | "peer_disconnected") { return; }
446        #[derive(Serialize)]
447        struct Record {
448            schema: &'static str, event: &'static str, decision_point: &'static str,
449            pid: u32, process_instance: u128, local_request: u64,
450            sequence: u64, monotonic_ns: u128, cause: &'static str,
451            context: saddle_core::request_context::RequestContextSnapshot,
452        }
453        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
454        let _ = self.submit_fixed_record(&Record {
455            schema: "request-stop/1", event: "request_stop_decided",
456            decision_point: "handler_select_before_dispatch_drop", pid: std::process::id(),
457            process_instance: finalization_process_instance(), local_request: context.local_request_id(),
458            sequence, monotonic_ns, cause, context,
459        });
460    }
461    /// Low-level bounded projection. Root callers transfer source identity via
462    /// the consuming runtime ReservedSupervisionFailure adapter, not capture.
463    #[doc(hidden)]
464    pub fn record_encoding_selection_association(&self,
465        context: saddle_core::request_context::RequestContextSnapshot,
466        original_deadline_unix_ms: i64,
467        source: Option<(saddle_core::DiagnosticOccurrence, saddle_core::RequestExecutionView)>) -> DiagnosticSubmission {
468        #[derive(Serialize)]
469        struct Record<'a> {
470            schema: &'static str, event: &'static str, decision_point: &'static str,
471            pid: u32, process_instance: u128, local_request: u64,
472            sequence: u64, monotonic_ns: u128, original_deadline_unix_ms: i64,
473            source_association: &'static str,
474            source_occurrence: Option<saddle_core::DiagnosticOccurrence>,
475            source_context: Option<saddle_core::request_context::UnifiedContext<'a>>,
476            context: saddle_core::request_context::RequestContextSnapshot,
477        }
478        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
479        crate::root_diagnostic::original::write_encoding_selection(self, &Record {
480            schema: "encoding-terminal-source/1", event: "encoding_terminal_selected",
481            decision_point: "encoding_response_selection", pid: std::process::id(),
482            process_instance: finalization_process_instance(), local_request: context.local_request_id(),
483            sequence, monotonic_ns, original_deadline_unix_ms,
484            source_association: if source.is_some() { "original_timeout" } else { "unavailable" },
485            source_occurrence: source.as_ref().map(|value| value.0),
486            source_context: source.as_ref().map(|value| value.1.unified_context()), context,
487        })
488    }
489    #[doc(hidden)]
490    pub fn rpc_physical_owner_receipt(&self,
491        context: saddle_core::request_context::RequestContextSnapshot, call_id: &str)
492        -> Option<RpcPhysicalOwnerReceipt> {
493        if call_id.is_empty() || call_id.len() > 256 { return None; }
494        let mut copied = [0; 256];
495        copied[..call_id.len()].copy_from_slice(call_id.as_bytes());
496        Some(RpcPhysicalOwnerReceipt { context, output: self.clone(),
497            process_instance: finalization_process_instance(), call_id: copied, call_id_len: call_id.len() })
498    }
499    #[doc(hidden)]
500    pub fn prepare_rpc_stage_release(&self,
501        context: saddle_core::request_context::RequestContextSnapshot,
502        storage: &saddle_admission::ProcessLogStorage, call_id: &str)
503        -> Option<saddle_admission::RpcStageReleaseObservation> {
504        // Reject observation only; never truncate identity or change RPC behavior.
505        if call_id.is_empty() || call_id.len() > 256 { return None; }
506        let mut copied = [0u8; 256];
507        copied[..call_id.len()].copy_from_slice(call_id.as_bytes());
508        saddle_admission::RpcStageReleaseObservation::prepare(storage, DetachedRpcStageRelease {
509            context, output: self.clone(), process_instance: finalization_process_instance(),
510            call_id: copied, call_id_len: call_id.len(),
511        }).ok()
512    }
513    #[doc(hidden)]
514    pub fn record_shutdown_signal_received(&self, application: saddle_core::ContextLabel, kind: &'static str, received: bool) {
515        #[derive(Serialize)]
516        struct Record {
517            schema: &'static str, pid: u32, process_instance: u128, application: saddle_core::ContextLabel,
518            signal: &'static str, received: bool, sequence: u64, monotonic_ns: u128,
519        }
520        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
521        let _ = self.submit_fixed_record(&Record {
522            schema: "process-signal-receipt/1", pid: std::process::id(),
523            process_instance: finalization_process_instance(), application,
524            signal: kind, received, sequence, monotonic_ns,
525        });
526    }
527    #[doc(hidden)]
528    pub fn record_request_task_first_poll(&self,
529        context: saddle_core::request_context::RequestContextSnapshot) {
530        #[derive(Serialize)]
531        struct Record {
532            schema: &'static str, event: &'static str, pid: u32, process_instance: u128,
533            local_request: u64, sequence: u64, monotonic_ns: u128,
534            context: saddle_core::request_context::RequestContextSnapshot,
535        }
536        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
537        let _ = self.submit_fixed_record(&Record {
538            schema: "request-task-execution/1", event: "before_first_body_poll",
539            pid: std::process::id(), process_instance: finalization_process_instance(),
540            local_request: context.local_request_id(), sequence, monotonic_ns, context,
541        });
542    }
543    #[doc(hidden)]
544    pub fn record_request_lifecycle(&self, application: &str, event: &'static str,
545        observed_phase: u8, active_claims: usize) {
546        #[derive(Serialize)]
547        struct Record<'a> {
548            schema: &'static str, pid: u32, process_instance: u128,
549            application: &'a str, event: &'static str, observed_phase: u8,
550            active_claims: usize, sequence: u64, monotonic_ns: u128,
551        }
552        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
553        let _ = self.submit_fixed_record(&Record {
554            schema: "request-lifecycle-boundary/1", pid: std::process::id(),
555            process_instance: finalization_process_instance(), application, event,
556            observed_phase, active_claims, sequence, monotonic_ns,
557        });
558    }
559    #[doc(hidden)]
560    pub fn record_delivery_attempt(&self, context: saddle_core::request_context::RequestContextSnapshot,
561        attempt_id: u64, phase: &'static str, write_starts: u64, write_completions: u64,
562        bytes_written: u64, delivery: saddle_core::ResponseDelivery) {
563        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
564        if sequence == 0 { return; }
565        #[derive(Serialize)]
566        struct Record {
567            schema: &'static str, pid: u32, process_instance: u128, local_request: u64,
568            sequence: u64, monotonic_ns: u128,
569            attempt_id: u64, phase: &'static str, write_starts: u64, write_completions: u64,
570            bytes_written: u64, delivery: saddle_core::ResponseDelivery,
571            context: saddle_core::request_context::RequestContextSnapshot,
572        }
573        let _ = self.submit_fixed_record(&Record {
574            schema: "request-delivery-attempt/1", pid: std::process::id(),
575            process_instance: finalization_process_instance(), local_request: context.local_request_id(),
576            sequence, monotonic_ns, attempt_id, phase, write_starts, write_completions, bytes_written, delivery, context,
577        });
578    }
579    #[doc(hidden)]
580    pub fn record_request_database_admission(&self,
581        context: saddle_core::request_context::RequestContextSnapshot,
582        ledger_instance: u64, account: saddle_admission::AccountIdentity,
583        accepted: bool, reason: &'static str) {
584        #[derive(Serialize)]
585        struct Record {
586            schema: &'static str, event: &'static str, pid: u32, process_instance: u128,
587            local_request: u64, ledger_instance: u64, account: saddle_admission::AccountIdentity,
588            accepted: bool, reason: &'static str, sequence: u64, monotonic_ns: u128,
589            context: saddle_core::request_context::RequestContextSnapshot,
590        }
591        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
592        let _ = self.submit_fixed_record(&Record {
593            schema: "request-database-admission/1", event: "database_stage_attempt",
594            pid: std::process::id(), process_instance: finalization_process_instance(),
595            local_request: context.local_request_id(), ledger_instance, account, accepted, reason,
596            sequence, monotonic_ns, context,
597        });
598    }
599    #[doc(hidden)]
600    pub fn record_request_task_owner(&self,
601        context: saddle_core::request_context::RequestContextSnapshot,
602        ledger_instance: u64, account: saddle_admission::AccountIdentity, task_role: &'static str) {
603        #[derive(Serialize)]
604        struct Record {
605            schema: &'static str, event: &'static str, pid: u32, process_instance: u128,
606            local_request: u64, ledger_instance: u64, account: saddle_admission::AccountIdentity,
607            task_role: &'static str, sequence: u64, monotonic_ns: u128,
608            context: saddle_core::request_context::RequestContextSnapshot,
609        }
610        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
611        let _ = self.submit_fixed_record(&Record {
612            schema: "request-task-roster/2", event: "task_owner_bound", pid: std::process::id(),
613            process_instance: finalization_process_instance(), local_request: context.local_request_id(),
614            ledger_instance, account, task_role, sequence, monotonic_ns, context,
615        });
616    }
617    #[doc(hidden)]
618    pub fn record_request_read_owner(&self,
619        context: saddle_core::request_context::RequestContextSnapshot,
620        ledger_instance: u64, account: saddle_admission::AccountIdentity,
621        peer: std::net::SocketAddrV4) {
622        #[derive(Serialize)]
623        struct Record {
624            schema: &'static str, event: &'static str, pid: u32, process_instance: u128,
625            local_request: u64, ledger_instance: u64, account: saddle_admission::AccountIdentity,
626            peer_address: [u8; 4], peer_port: u16, sequence: u64, monotonic_ns: u128,
627            context: saddle_core::request_context::RequestContextSnapshot,
628        }
629        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
630        let _ = self.submit_fixed_record(&Record {
631            schema: "request-read-owner/1", event: "recognition_read_head_entered",
632            pid: std::process::id(), process_instance: finalization_process_instance(),
633            local_request: context.local_request_id(), ledger_instance, account,
634            peer_address: peer.ip().octets(), peer_port: peer.port(), sequence, monotonic_ns, context,
635        });
636    }
637    #[doc(hidden)]
638    pub fn record_request_account_identity(&self, view: &saddle_core::RequestExecutionView,
639        permit: &saddle_admission::StoragePermit) {
640        let Some(account) = permit.release_account_identity() else { return; };
641        let context = view.diagnostic_snapshot();
642        #[derive(Serialize)]
643        struct Record<'a> {
644            schema: &'static str, event: &'static str, pid: u32, process_instance: u128,
645            local_request: u64, ledger_instance: u64, account: saddle_admission::AccountIdentity,
646            context: &'a saddle_core::request_context::RequestContextSnapshot,
647        }
648        let _ = self.submit_fixed_record(&Record {
649            schema: "request-account-identity/1", event: "request_account_bound",
650            pid: std::process::id(), process_instance: finalization_process_instance(),
651            local_request: context.local_request_id(), ledger_instance: permit.diagnostic_ledger_instance(),
652            account, context: &context,
653        });
654    }
655    #[doc(hidden)]
656    pub fn prepare_object_lifetime(&self, view: &saddle_core::RequestExecutionView,
657        storage: &saddle_admission::ProcessLogStorage, kind: &'static str)
658        -> Option<saddle_admission::ObjectLifetimeObservation> {
659        if !matches!(kind, "raw_body" | "typed_input" | "db_parameter_text") { return None; }
660        saddle_admission::ObjectLifetimeObservation::prepare(storage, DetachedObjectLifetime {
661            context: view.diagnostic_snapshot(), output: self.clone(),
662            process_instance: finalization_process_instance(), kind,
663        }).ok()
664    }
665    /// The copy contains only encoded identity, not a root/account reference.
666    /// Its box is prepaid independently from the account being observed.
667    #[doc(hidden)]
668    pub fn prepare_account_release(&self, view: &saddle_core::RequestExecutionView,
669        storage: &saddle_admission::ProcessLogStorage)
670        -> Option<saddle_admission::AccountReleaseObservation> {
671        // Reuse the bounded, detached identity snapshot. Parsing encoded JSON
672        // here would allocate after the observed account's audit identity ends.
673        let context = view.diagnostic_snapshot();
674        saddle_admission::AccountReleaseObservation::prepare(storage, DetachedAccountRelease {
675            context, output: self.clone(), process_instance: finalization_process_instance(),
676        }).ok()
677    }
678}
679
680// One existing queue/writer; inline admitted records allocate no payload blocks.
681#[allow(clippy::large_enum_variant)] // Boxing here would allocate on admitted submission.
682enum DiagnosticPacket {
683    Startup(Vec<u8>, Option<saddle_core::DeferredDiagnosticStack>),
684    Admitted(FixedDiagnosticBytes),
685    Original(FixedDiagnosticBytes),
686    ConfirmedOriginal(FixedDiagnosticBytes, u64),
687}
688impl DiagnosticPacket {
689    fn bytes(&self) -> &[u8] {
690        match self {
691            Self::Startup(v, _) => v,
692            Self::Admitted(v) | Self::Original(v) | Self::ConfirmedOriginal(v, _) => &v.bytes[..v.len],
693        }
694    }
695    fn resolve_on_worker(&mut self, status: &Status) {
696        let Self::Startup(bytes, stack) = self else {
697            return;
698        };
699        let Some(stack) = stack.take() else {
700            return;
701        };
702        let resolved = stack.resolve_on_output_worker();
703        let Ok(mut record) = serde_json::from_slice::<serde_json::Value>(bytes) else {
704            status.dropped.fetch_add(1, Ordering::Relaxed);
705            return;
706        };
707        record["diagnostic"]["stack_status"] = resolved.stack_status.into();
708        record["diagnostic"]["stack"] = resolved.stack.into();
709        record["diagnostic"]["stack_truncated"] = resolved.stack_truncated.into();
710        if resolved.stack_truncated {
711            status.truncated.fetch_add(1, Ordering::Relaxed);
712        }
713        let mut encoded = LimitedBytes(Vec::with_capacity(RECORD_BYTES));
714        if serde_json::to_writer(&mut encoded, &record).is_err() {
715            encoded.0.clear();
716            record["diagnostic"]["stack"] = "".into();
717            record["diagnostic"]["stack_status"] = "omitted_output_limit".into();
718            record["diagnostic"]["stack_truncated"] = true.into();
719            record["encoding_truncated"] = true.into();
720            status.truncated.fetch_add(1, Ordering::Relaxed);
721            if serde_json::to_writer(&mut encoded, &record).is_err() {
722                // Original bounded row remains truthful: captured_unresolved.
723                return;
724            }
725        }
726        encoded.0.push(b'\n');
727        *bytes = encoded.0;
728    }
729}
730#[derive(Clone)]
731pub(crate) struct FixedDiagnosticBytes {
732    pub(crate) bytes: [u8; 8192],
733    pub(crate) len: usize,
734}
735pub(crate) fn root_frame_layouts() -> (std::alloc::Layout, std::alloc::Layout, usize) {
736    (
737        std::alloc::Layout::new::<FixedDiagnosticBytes>(),
738        std::alloc::Layout::new::<DiagnosticPacket>(),
739        QUEUE_CAPACITY,
740    )
741}
742/// One process-owned allocation, shared by every handle and confirmation.
743/// Includes the Arc counters, confirmation slot and bounded takeover target.
744pub(crate) fn root_status_owner_layout() -> std::alloc::Layout {
745    // Mirrors alloc::sync::ArcInner in the pinned 1.95.0 toolchain.
746    #[repr(C, align(2))]
747    struct ArcOwner<T> { strong: std::sync::atomic::AtomicUsize, weak: std::sync::atomic::AtomicUsize, value: T }
748    std::alloc::Layout::new::<ArcOwner<Status>>()
749}
750impl Write for FixedDiagnosticBytes {
751    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
752        if bytes.len() > self.bytes.len().saturating_sub(self.len + 1) {
753            return Err(io::ErrorKind::WriteZero.into());
754        }
755        self.bytes[self.len..self.len + bytes.len()].copy_from_slice(bytes);
756        self.len += bytes.len();
757        Ok(bytes.len())
758    }
759    fn flush(&mut self) -> io::Result<()> {
760        Ok(())
761    }
762}
763
764#[derive(Debug)]
765pub enum EmergencyInitError {
766    InvalidDirectory,
767    AlreadyActive,
768    Open(io::Error),
769    Spawn(io::ErrorKind),
770}
771impl fmt::Display for EmergencyInitError {
772    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
773        write!(f, "emergency diagnostic initialization failed: {self:?}")
774    }
775}
776impl std::error::Error for EmergencyInitError {
777    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
778        match self { Self::Open(error) => Some(error), _ => None }
779    }
780}
781
782struct LimitedBytes(Vec<u8>);
783impl Write for LimitedBytes {
784    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
785        if self.0.len().saturating_add(bytes.len()) > RECORD_BYTES - 1 {
786            return Err(io::Error::new(
787                io::ErrorKind::InvalidData,
788                "diagnostic encoding bound",
789            ));
790        }
791        self.0.extend_from_slice(bytes);
792        Ok(bytes.len())
793    }
794    fn flush(&mut self) -> io::Result<()> {
795        Ok(())
796    }
797}
798#[derive(Serialize)]
799struct Envelope<'a> {
800    event: &'static str,
801    timestamp_unix_ms: u128,
802    diagnostic: &'a Diagnostic,
803    trace_id: Option<&'a str>,
804    rpc_id: Option<String>,
805    span_id: Option<String>,
806    operation: Option<String>,
807    request: Option<&'a str>,
808    route: Option<&'a str>,
809}
810
811impl<'a> Envelope<'a> {
812    fn new(
813        diagnostic: &'a Diagnostic,
814        context: Option<(&'a CallContext, &'a crate::EventContext)>,
815    ) -> Self {
816        Self {
817            event: "framework.diagnostic",
818            timestamp_unix_ms: std::time::SystemTime::now()
819                .duration_since(std::time::UNIX_EPOCH)
820                .unwrap_or_default()
821                .as_millis(),
822            diagnostic,
823            trace_id: context.map(|(call, _)| call.trace_correlation_id().as_str()),
824            rpc_id: context
825                .and_then(|(call, _)| call.rpc_correlation_id().map(|rpc| rpc.as_str().to_owned())),
826            span_id: context.map(|(call, _)| call.span_id().to_string()),
827            operation: context.map(|(call, _)| call.operation().to_string()),
828            request: context.map(|(_, event)| event.diagnostic_request()),
829            route: context.map(|(_, event)| event.diagnostic_route()),
830        }
831    }
832}
833
834impl EmergencyDiagnosticHandle {
835    /// Allocation-free admitted-path encoding and try-submit. No source callback,
836    /// stack capture, filesystem IO, or modification of the caller's Result.
837    /// Diagnostic and axes remain with the caller on every outcome.
838    pub fn submit_bounded(
839        &self,
840        diagnostic: Option<&saddle_core::BoundedDiagnostic>,
841        axes: &saddle_core::DiagnosticOutcomeAxes,
842        context: Option<(&CallContext, &crate::EventContext)>,
843    ) -> DiagnosticSubmission {
844        self.submit_bounded_parts(
845            diagnostic,
846            axes,
847            context.map(|(c, e)| (c, e.diagnostic_request(), e.diagnostic_route())),
848            None,
849        )
850    }
851    pub(crate) fn submit_bounded_parts(
852        &self,
853        diagnostic: Option<&saddle_core::BoundedDiagnostic>,
854        axes: &saddle_core::DiagnosticOutcomeAxes,
855        context: Option<(&CallContext, &str, &str)>,
856        stage: Option<(&'static str, u64)>,
857    ) -> DiagnosticSubmission {
858        self.encode_bounded(
859            if stage.is_some() { None } else { diagnostic },
860            diagnostic.map(|d| d.occurrence()),
861            axes,
862            context,
863            stage,
864            stage.is_some(),
865        )
866    }
867    /// Boundary outcome only; the source owns and submits the detailed diagnostic.
868    /// An occurrence does not assert that the source record reached storage.
869    pub fn submit_boundary(
870        &self,
871        occurrence: Option<saddle_core::DiagnosticOccurrence>,
872        axes: &saddle_core::DiagnosticOutcomeAxes,
873        context: Option<(&CallContext, &crate::EventContext)>,
874    ) -> DiagnosticSubmission {
875        self.encode_bounded(
876            None,
877            occurrence,
878            axes,
879            context.map(|(c, e)| (c, e.diagnostic_request(), e.diagnostic_route())),
880            None,
881            true,
882        )
883    }
884    pub(crate) fn submit_boundary_parts(
885        &self,
886        occurrence: Option<saddle_core::DiagnosticOccurrence>,
887        axes: &saddle_core::DiagnosticOutcomeAxes,
888        context: Option<(&CallContext, &str, &str)>,
889        stage: Option<(&'static str, u64)>,
890    ) -> DiagnosticSubmission {
891        self.encode_bounded(None, occurrence, axes, context, stage, true)
892    }
893    #[allow(clippy::too_many_arguments)]
894    fn encode_bounded(
895        &self,
896        diagnostic: Option<&saddle_core::BoundedDiagnostic>,
897        occurrence: Option<saddle_core::DiagnosticOccurrence>,
898        axes: &saddle_core::DiagnosticOutcomeAxes,
899        context: Option<(&CallContext, &str, &str)>,
900        stage: Option<(&'static str, u64)>,
901        boundary: bool,
902    ) -> DiagnosticSubmission {
903        self.status.submitting.fetch_add(1, Ordering::AcqRel);
904        struct Exit<'a>(&'a AtomicU64);
905        impl Drop for Exit<'_> {
906            fn drop(&mut self) {
907                self.0.fetch_sub(1, Ordering::Release);
908            }
909        }
910        let _exit = Exit(&self.status.submitting);
911        // Fixed scan/encoding work even if a caller supplied an oversized ID.
912        if context.is_some_and(|(c, request, route)| {
913            c.operation().as_str().len() > 256 || request.len() > 128 || route.len() > 192
914        }) {
915            self.status.dropped.fetch_add(1, Ordering::Relaxed);
916            return DiagnosticSubmission::EncodingFailed;
917        }
918        if self.status.closed.load(Ordering::Acquire) {
919            self.status.dropped.fetch_add(1, Ordering::Relaxed);
920            return DiagnosticSubmission::Closed;
921        }
922        #[derive(Serialize)]
923        struct Record<'a> {
924            event: &'static str,
925            stage: Option<&'static str>,
926            elapsed_ms: Option<u64>,
927            timestamp_unix_ms: u128,
928            diagnostic: Option<&'a saddle_core::BoundedDiagnostic>,
929            diagnostic_reference: Option<saddle_core::DiagnosticOccurrence>,
930            axes: &'a saddle_core::DiagnosticOutcomeAxes,
931            trace_id: Option<&'a str>,
932            rpc_id: Option<&'a str>,
933            span_id: Option<&'a str>,
934            operation: Option<&'a str>,
935            request: Option<&'a str>,
936            route: Option<&'a str>,
937        }
938        let mut span = [b'0'; 16];
939        if let Some((call, _, _)) = context {
940            let value = call.span_id().as_u64();
941            for (i, byte) in span.iter_mut().enumerate() {
942                *byte = b"0123456789abcdef"[((value >> ((15 - i) * 4)) & 15) as usize];
943            }
944        }
945        let record = Record {
946            event: if stage.is_some() {
947                "framework.stage.finished"
948            } else if boundary {
949                "framework.boundary.outcome"
950            } else {
951                "framework.diagnostic"
952            },
953            stage: stage.map(|s| s.0),
954            elapsed_ms: stage.map(|s| s.1),
955            timestamp_unix_ms: std::time::SystemTime::now()
956                .duration_since(std::time::UNIX_EPOCH)
957                .unwrap_or_default()
958                .as_millis(),
959            diagnostic,
960            diagnostic_reference: occurrence,
961            axes,
962            trace_id: context.map(|(c, _, _)| c.trace_correlation_id().as_str()),
963            rpc_id: context.and_then(|(c, _, _)| c.rpc_correlation_id().map(|r| r.as_str())),
964            span_id: context.and_then(|_| std::str::from_utf8(&span).ok()),
965            operation: context.map(|(c, _, _)| c.operation().as_str()),
966            request: context.map(|(_, request, _)| request),
967            route: context.map(|(_, _, route)| route),
968        };
969        self.encode_and_queue(&record)
970    }
971    /// Internal shared bounded encoder. The request adapter owns its sealed receipt.
972    pub(crate) fn submit_fixed_record<T: Serialize>(&self, record: &T) -> DiagnosticSubmission {
973        self.status.submitting.fetch_add(1, Ordering::AcqRel);
974        let result = if self.status.closed.load(Ordering::Acquire) {
975            self.status.dropped.fetch_add(1, Ordering::Relaxed);
976            DiagnosticSubmission::Closed
977        } else {
978            self.encode_and_queue(record)
979        };
980        self.status.submitting.fetch_sub(1, Ordering::Release);
981        result
982    }
983    fn encode_and_queue<T: Serialize>(&self, record: &T) -> DiagnosticSubmission {
984        self.encode_and_queue_fallback(record, None, false)
985    }
986    pub(crate) fn submit_original_record<T: Serialize>(&self, record: &T) -> DiagnosticSubmission {
987        self.status.submitting.fetch_add(1, Ordering::AcqRel);
988        let result = if self.status.closed.load(Ordering::Acquire) {
989            self.status.dropped.fetch_add(1, Ordering::Relaxed);
990            DiagnosticSubmission::Closed
991        } else {
992            self.encode_and_queue_fallback(record, None, true)
993        };
994        self.status.submitting.fetch_sub(1, Ordering::Release);
995        result
996    }
997    /// A source segment is acknowledged only after the writer has written it.
998    /// This is deliberately separate from the legacy, lossy original path.
999    pub(crate) fn write_original_record<T: Serialize>(&self, record: &T) -> io::Result<()> {
1000        self.status.submitting.fetch_add(1, Ordering::AcqRel);
1001        struct Exit<'a>(&'a AtomicU64);
1002        impl Drop for Exit<'_> {
1003            fn drop(&mut self) { self.0.fetch_sub(1, Ordering::Release); }
1004        }
1005        let _exit = Exit(&self.status.submitting);
1006        let mut bytes = FixedDiagnosticBytes { bytes: [0; 8192], len: 0 };
1007        serde_json::to_writer(&mut bytes, record).map_err(io::Error::other)?;
1008        bytes.bytes[bytes.len] = b'\n';
1009        bytes.len += 1;
1010        let lease = self.status.acquire_confirmation();
1011        let retry = bytes.clone();
1012        match self.sender.send(DiagnosticPacket::ConfirmedOriginal(bytes, lease.sequence)) {
1013            Ok(()) => {
1014                self.status.enqueued.fetch_add(1, Ordering::Relaxed);
1015                match lease.wait() {
1016                    Err(error) if error.kind() == io::ErrorKind::BrokenPipe
1017                        && self.status.worker_stopped.load(Ordering::Acquire) => {
1018                        self.write_original_after_worker(&retry, false)
1019                    }
1020                    result => result,
1021                }
1022            }
1023            Err(_) => self.write_original_after_worker(&retry, true),
1024        }
1025    }
1026    /// The worker has exited. Use its pinned append target while the borrowed
1027    /// source still exists; only a failed initial open needs a fresh open.
1028    fn write_original_after_worker(&self, bytes: &FixedDiagnosticBytes, count_admission: bool) -> io::Result<()> {
1029        if !self.status.worker_stopped.load(Ordering::Acquire) {
1030            return Err(io::Error::new(io::ErrorKind::BrokenPipe, "diagnostic writer unavailable"));
1031        }
1032        let path = self.status.fallback_path.get().ok_or_else(||
1033            io::Error::new(io::ErrorKind::NotFound, "diagnostic output target unavailable"))?;
1034        let mut fallback = self.status.fallback_file.lock().unwrap_or_else(|e| e.into_inner());
1035        if fallback.is_none() {
1036            *fallback = Some(open_emergency_file(path.as_path(), false)?);
1037        }
1038        fallback.as_mut().expect("fallback file opened").write_all(&bytes.bytes[..bytes.len])?;
1039        if count_admission { self.status.enqueued.fetch_add(1, Ordering::Relaxed); }
1040        self.status.written.fetch_add(1, Ordering::Release);
1041        Ok(())
1042    }
1043    pub(crate) fn submit_fixed_with_fallback<T: Serialize>(
1044        &self,
1045        record: &T,
1046        fallback: &T,
1047    ) -> DiagnosticSubmission {
1048        self.status.submitting.fetch_add(1, Ordering::AcqRel);
1049        let result = if self.status.closed.load(Ordering::Acquire) {
1050            self.status.dropped.fetch_add(1, Ordering::Relaxed);
1051            DiagnosticSubmission::Closed
1052        } else {
1053            self.encode_and_queue_fallback(record, Some(fallback), false)
1054        };
1055        self.status.submitting.fetch_sub(1, Ordering::Release);
1056        result
1057    }
1058    fn encode_and_queue_fallback<T: Serialize>(
1059        &self,
1060        record: &T,
1061        fallback: Option<&T>,
1062        original: bool,
1063    ) -> DiagnosticSubmission {
1064        let mut bytes = FixedDiagnosticBytes {
1065            bytes: [0; 8192],
1066            len: 0,
1067        };
1068        if serde_json::to_writer(&mut bytes, record).is_err() {
1069            bytes.len = 0;
1070            let recovered = fallback
1071                .is_some_and(|fallback| serde_json::to_writer(&mut bytes, fallback).is_ok());
1072            if !recovered {
1073                self.status.dropped.fetch_add(1, Ordering::Relaxed);
1074                return DiagnosticSubmission::EncodingFailed;
1075            }
1076            self.status.truncated.fetch_add(1, Ordering::Relaxed);
1077        }
1078        bytes.bytes[bytes.len] = b'\n';
1079        bytes.len += 1;
1080        let packet = if original {
1081            DiagnosticPacket::Original(bytes)
1082        } else {
1083            DiagnosticPacket::Admitted(bytes)
1084        };
1085        match self.sender.try_send(packet) {
1086            Ok(()) => {
1087                self.status.enqueued.fetch_add(1, Ordering::Relaxed);
1088                DiagnosticSubmission::Enqueued
1089            }
1090            Err(TrySendError::Full(_)) => {
1091                self.status.dropped.fetch_add(1, Ordering::Relaxed);
1092                DiagnosticSubmission::Full
1093            }
1094            Err(TrySendError::Disconnected(_)) => {
1095                self.status.dropped.fetch_add(1, Ordering::Relaxed);
1096                DiagnosticSubmission::Closed
1097            }
1098        }
1099    }
1100    pub fn snapshot(&self) -> DiagnosticOutputSnapshot {
1101        self.status.snapshot()
1102    }
1103    /// Enqueued means accepted in memory, NOT written or durable.
1104    pub fn submit(&self, diagnostic: &Diagnostic) -> DiagnosticSubmission {
1105        self.submit_context(diagnostic, None)
1106    }
1107    pub fn submit_context(
1108        &self,
1109        diagnostic: &Diagnostic,
1110        context: Option<(&CallContext, &crate::EventContext)>,
1111    ) -> DiagnosticSubmission {
1112        self.submit_existing_record(
1113            &Envelope::new(diagnostic, context),
1114            diagnostic.deferred_stack(),
1115        )
1116    }
1117    // Retains the existing 32KiB legacy encoding/truncation behavior. This is
1118    // NOT the allocation-free admitted bounded encoder.
1119    pub(crate) fn submit_existing_record<T: Serialize>(
1120        &self,
1121        record: &T,
1122        stack: Option<saddle_core::DeferredDiagnosticStack>,
1123    ) -> DiagnosticSubmission {
1124        let active = self.status.submitting.fetch_add(1, Ordering::AcqRel);
1125        struct Exit<'a>(&'a AtomicU64);
1126        impl Drop for Exit<'_> {
1127            fn drop(&mut self) {
1128                self.0.fetch_sub(1, Ordering::AcqRel);
1129            }
1130        }
1131        let _exit = Exit(&self.status.submitting);
1132        if active >= QUEUE_CAPACITY as u64 {
1133            self.status.dropped.fetch_add(1, Ordering::Relaxed);
1134            return DiagnosticSubmission::Full;
1135        }
1136        if self.status.closed.load(Ordering::Acquire) {
1137            self.status.dropped.fetch_add(1, Ordering::Relaxed);
1138            return DiagnosticSubmission::Closed;
1139        }
1140        let mut bytes = LimitedBytes(Vec::with_capacity(RECORD_BYTES));
1141        if serde_json::to_writer(&mut bytes, record).is_err() {
1142            bytes.0.clear();
1143            let mut reduced = match serde_json::to_value(record) {
1144                Ok(value) => value,
1145                Err(_) => {
1146                    self.status.dropped.fetch_add(1, Ordering::Relaxed);
1147                    return DiagnosticSubmission::EncodingFailed;
1148                }
1149            };
1150            reduced["diagnostic"]["stack"] = serde_json::Value::String(String::new());
1151            reduced["diagnostic"]["stack_truncated"] = true.into();
1152            reduced["encoding_truncated"] = true.into();
1153            self.status.truncated.fetch_add(1, Ordering::Relaxed);
1154            if serde_json::to_writer(&mut bytes, &reduced).is_err() {
1155                self.status.dropped.fetch_add(1, Ordering::Relaxed);
1156                return DiagnosticSubmission::EncodingFailed;
1157            }
1158        }
1159        bytes.0.push(b'\n');
1160        match self
1161            .sender
1162            .try_send(DiagnosticPacket::Startup(bytes.0, stack))
1163        {
1164            Ok(()) => {
1165                self.status.enqueued.fetch_add(1, Ordering::Relaxed);
1166                DiagnosticSubmission::Enqueued
1167            }
1168            Err(TrySendError::Full(_)) => {
1169                self.status.dropped.fetch_add(1, Ordering::Relaxed);
1170                DiagnosticSubmission::Full
1171            }
1172            Err(TrySendError::Disconnected(_)) => {
1173                self.status.dropped.fetch_add(1, Ordering::Relaxed);
1174                DiagnosticSubmission::Closed
1175            }
1176        }
1177    }
1178}
1179
1180impl crate::Observer {
1181    /// Mirrors one safe diagnostic into independent emergency output and the
1182    /// existing logger. The result acknowledges only emergency queue admission.
1183    pub fn record_diagnostic(
1184        &self,
1185        diagnostic: &Diagnostic,
1186        emergency: &EmergencyDiagnosticHandle,
1187        context: Option<(&CallContext, &crate::EventContext)>,
1188    ) -> DiagnosticSubmission {
1189        let result = emergency.submit_context(diagnostic, context);
1190        self.mirror_existing_record(&Envelope::new(diagnostic, context), diagnostic.category());
1191        result
1192    }
1193    pub(crate) fn mirror_existing_record<T: Serialize>(
1194        &self,
1195        record: &T,
1196        category: saddle_core::DiagnosticCategory,
1197    ) {
1198        if let Ok(serde_json::Value::Object(fields)) = serde_json::to_value(record) {
1199            let level = if matches!(category, saddle_core::DiagnosticCategory::ExpectedRejection) {
1200                crate::EventLevel::Info
1201            } else {
1202                crate::EventLevel::Error
1203            };
1204            let mut record = crate::logger::LogRecord::new(level, "framework.diagnostic");
1205            record.data = fields;
1206            record.data.remove("event");
1207            record.data.remove("timestamp_unix_ms");
1208            self.emit(record);
1209        }
1210    }
1211}
1212fn open_emergency_file(path: &std::path::Path, create_parent: bool) -> io::Result<File> {
1213    if create_parent {
1214        if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; }
1215    }
1216    // The worker and its bounded takeover use the same verified target.
1217    use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
1218    let file = OpenOptions::new()
1219        .create(true)
1220        .append(true)
1221        .mode(0o644)
1222        .custom_flags(
1223            rustix::fs::OFlags::NOFOLLOW.bits() as i32
1224                | rustix::fs::OFlags::NONBLOCK.bits() as i32,
1225        )
1226        .open(path)?;
1227    if !file.metadata()?.is_file() {
1228        return Err(io::Error::new(io::ErrorKind::InvalidInput, "non-regular emergency file"));
1229    }
1230    rustix::fs::flock(&file, rustix::fs::FlockOperation::NonBlockingLockExclusive)
1231        .map_err(io::Error::from)?;
1232    file.set_permissions(fs::Permissions::from_mode(0o644))?;
1233    Ok(file)
1234}
1235
1236fn validate_emergency_directory(directory: &std::path::Path) -> Result<(), EmergencyInitError> {
1237    let directory = directory.to_str().ok_or(EmergencyInitError::InvalidDirectory)?;
1238    if directory.is_empty()
1239        || directory.len() > 4096
1240        || directory.chars().any(char::is_control)
1241        || directory.contains("://")
1242        || directory.contains(['@', '?', '#'])
1243    {
1244        return Err(EmergencyInitError::InvalidDirectory);
1245    }
1246    Ok(())
1247}
1248
1249impl EmergencyDiagnostics {
1250    /// Directory setup is performed by the worker, never the submitting request.
1251    /// Callers must check snapshot/written, not equate successful start with IO readiness.
1252    pub fn start(config: &FileLoggingConfig) -> Result<Self, EmergencyInitError> {
1253        Self::start_inner(config, false)
1254    }
1255
1256    /// Startup preopens the verified target while its raw I/O error can still
1257    /// be written to the independent bootstrap original file.
1258    pub fn start_checked(config: &FileLoggingConfig) -> Result<Self, EmergencyInitError> {
1259        Self::start_inner(config, true)
1260    }
1261
1262    fn start_inner(config: &FileLoggingConfig, preopen: bool) -> Result<Self, EmergencyInitError> {
1263        validate_emergency_directory(config.directory())?;
1264        let target = config.directory().join(EMERGENCY_FILE_NAME);
1265        let fallback_path = BoundedTargetPath::new(&target)
1266            .map_err(|_| EmergencyInitError::InvalidDirectory)?;
1267        if ACTIVE
1268            .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
1269            .is_err()
1270        {
1271            return Err(EmergencyInitError::AlreadyActive);
1272        }
1273        let initial_file = if preopen {
1274            match open_emergency_file(&target, true) {
1275                Ok(file) => Some(file),
1276                Err(error) => {
1277                    ACTIVE.store(false, Ordering::Release);
1278                    return Err(EmergencyInitError::Open(error));
1279                }
1280            }
1281        } else { None };
1282        let path = target.clone();
1283        let (sender, receiver) = mpsc::sync_channel::<DiagnosticPacket>(QUEUE_CAPACITY);
1284        let status = Arc::new(Status::default());
1285        status.owns_active.store(true, Ordering::Release);
1286        assert!(status.fallback_path.set(fallback_path).is_ok(), "new diagnostic status");
1287        let worker_status = status.clone();
1288        let worker = thread::Builder::new()
1289            .name("saddle-emergency-writer".into())
1290            .spawn(move || {
1291                struct ActiveGuard(Arc<Status>);
1292                impl Drop for ActiveGuard {
1293                    fn drop(&mut self) {
1294                        let _state = self.0.confirmation.lock().unwrap_or_else(|e| e.into_inner());
1295                        self.0.worker_stopped.store(true, Ordering::Release);
1296                        self.0.confirmation_ready.notify_all();
1297                    }
1298                }
1299                let _active = ActiveGuard(worker_status.clone());
1300                let file = initial_file.map(Ok).unwrap_or_else(|| open_emergency_file(&path, true)).and_then(|file| {
1301                    // Keep the worker's locked inode for a bounded source
1302                    // takeover. Rotation cannot split one original across
1303                    // the old and new path between acknowledged segments.
1304                    let takeover = file.try_clone()?;
1305                    *worker_status.fallback_file.lock().unwrap_or_else(|e| e.into_inner()) = Some(takeover);
1306                    Ok(file)
1307                });
1308                if let Err(error) = &file {
1309                    worker_status.fail(1, error);
1310                }
1311                worker_status.initialized.store(true, Ordering::Release);
1312                let mut output = file;
1313                let mut stderr_attempted = false;
1314                loop {
1315                    match receiver.recv_timeout(Duration::from_millis(10)) {
1316                        Ok(mut bytes) => {
1317                            bytes.resolve_on_worker(&worker_status);
1318                            let delivery = deliver_with_disclosure(
1319                                &mut output,
1320                                &mut io::stderr(),
1321                                &path,
1322                                bytes.bytes(),
1323                                &worker_status,
1324                                &mut stderr_attempted,
1325                                !matches!(bytes, DiagnosticPacket::Original(_) | DiagnosticPacket::ConfirmedOriginal(_, _)),
1326                            );
1327                            if let DiagnosticPacket::ConfirmedOriginal(_, sequence) = bytes {
1328                                worker_status.acknowledge(sequence, delivery);
1329                            }
1330                        }
1331                        Err(mpsc::RecvTimeoutError::Timeout) => {
1332                            if worker_status.closed.load(Ordering::Acquire)
1333                                && worker_status.submitting.load(Ordering::Acquire) == 0
1334                            {
1335                                break;
1336                            }
1337                        }
1338                        Err(mpsc::RecvTimeoutError::Disconnected) => break,
1339                    }
1340                }
1341            })
1342            .map_err(|error| {
1343                status.owns_active.store(false, Ordering::Release);
1344                ACTIVE.store(false, Ordering::Release);
1345                EmergencyInitError::Spawn(error.kind())
1346            })?;
1347        Ok(Self {
1348            handle: EmergencyDiagnosticHandle { sender, status },
1349            worker: Some(worker),
1350            target,
1351            source_target_preopened: preopen,
1352        })
1353    }
1354    /// Issue the formal output capability only after checked startup opened
1355    /// the selected controlled file. A later writer failure is reported by
1356    /// the synchronous original write rather than by this capability.
1357    pub fn source_output(&self) -> Option<SourceOutput> {
1358        self.source_target_preopened.then(|| SourceOutput(self.handle()))
1359    }
1360    pub fn handle(&self) -> EmergencyDiagnosticHandle {
1361        self.handle.clone()
1362    }
1363    pub fn target(&self) -> &std::path::Path {
1364        &self.target
1365    }
1366    pub fn snapshot(&self) -> DiagnosticOutputSnapshot {
1367        self.handle.snapshot()
1368    }
1369    /// Call using the EXISTING lifecycle deadline; Pending is not a new budget.
1370    pub fn shutdown(&mut self) -> DiagnosticShutdown {
1371        self.handle.status.closed.store(true, Ordering::Release);
1372        if self
1373            .worker
1374            .as_ref()
1375            .is_some_and(|worker| !worker.is_finished())
1376        {
1377            return DiagnosticShutdown::Pending;
1378        }
1379        match self.worker.take().map(JoinHandle::join) {
1380            Some(Err(_)) => DiagnosticShutdown::WorkerPanicked,
1381            _ => DiagnosticShutdown::Finished,
1382        }
1383    }
1384}
1385impl Drop for EmergencyDiagnostics {
1386    fn drop(&mut self) {
1387        self.handle.status.closed.store(true, Ordering::Release);
1388    }
1389}
1390
1391#[cfg(test)]
1392fn deliver<W: Write, E: Write>(
1393    output: &mut io::Result<W>,
1394    stderr: &mut E,
1395    target: &std::path::Path,
1396    bytes: &[u8],
1397    status: &Status,
1398    stderr_attempted: &mut bool,
1399) {
1400    let _observed_result = deliver_with_disclosure(
1401        output,
1402        stderr,
1403        target,
1404        bytes,
1405        status,
1406        stderr_attempted,
1407        true,
1408    );
1409}
1410
1411#[allow(clippy::too_many_arguments)] // Original disclosure is independent of output reliability.
1412fn deliver_with_disclosure<W: Write, E: Write>(
1413    output: &mut io::Result<W>,
1414    stderr: &mut E,
1415    target: &std::path::Path,
1416    bytes: &[u8],
1417    status: &Status,
1418    stderr_attempted: &mut bool,
1419    allow_stderr_content: bool,
1420) -> io::Result<()> {
1421    let result = match output {
1422        Ok(writer) => writer.write_all(bytes),
1423        Err(error) => Err(error
1424            .raw_os_error()
1425            .map(io::Error::from_raw_os_error)
1426            .unwrap_or_else(|| io::Error::from(error.kind()))),
1427    };
1428    if let Err(ref error) = result {
1429        status.fail(2, &error);
1430        status.output_failed.fetch_add(1, Ordering::Relaxed);
1431        // Only the independent worker can block here. No recursive logger call.
1432        if !*stderr_attempted {
1433            *stderr_attempted = true;
1434            let failure = serde_json::json!({"event":"framework.diagnostic.output_failed", "target":target.to_string_lossy(), "io_kind":format!("{:?}", error.kind()), "os_code":error.raw_os_error()});
1435            if serde_json::to_writer(&mut *stderr, &failure)
1436                .and_then(|_| stderr.write_all(b"\n").map_err(serde_json::Error::io))
1437                .is_err()
1438                || (allow_stderr_content && stderr.write_all(bytes).is_err())
1439            {
1440                status.stderr_failed.fetch_add(1, Ordering::Relaxed);
1441            }
1442        } else {
1443            status.stderr_suppressed.fetch_add(1, Ordering::Relaxed);
1444        }
1445    } else {
1446        status.written.fetch_add(1, Ordering::Release);
1447    }
1448    result
1449}
1450
1451#[cfg(test)]
1452mod tests {
1453    use super::*;
1454
1455    #[test]
1456    fn finalization_maximum_identity_frame_and_queue_failures_are_bounded() {
1457        use saddle_core::request_context::{ContextFact, ContextLabel, RequestIdentityGroup,
1458            RequestLocalFacts, RequestRootPublisher, RequestViewPhase};
1459        let label = "a".repeat(256);
1460        let request = "\\".repeat(256);
1461        let call = saddle_core::CallContext::new(label.clone().into(), label.clone().into(),
1462            label.clone().into(), label.clone().into(), saddle_core::TraceId::from_u128(1),
1463            saddle_core::SpanId::from_u64(1));
1464        let mut publisher = RequestRootPublisher::create(ContextLabel::checked(&label).unwrap(), ContextFact::NotEstablished).unwrap();
1465        publisher.publish(RequestIdentityGroup::from_validated(&call, &request, &label, u32::MAX,
1466            ContextFact::Present(ContextLabel::checked(&label).unwrap())).unwrap()).ok().unwrap();
1467        let view = publisher.reference().view(RequestLocalFacts::new(RequestViewPhase::Reading));
1468        let mut context = FixedDiagnosticBytes { bytes: [0; 8192], len: 0 };
1469        serde_json::to_writer(&mut context, &view).unwrap();
1470        let (sender, receiver) = mpsc::sync_channel(1);
1471        let status = Arc::new(Status::default());
1472        let observer = DetachedAccountRelease { context: view.diagnostic_snapshot(), output: EmergencyDiagnosticHandle {
1473            sender, status: status.clone(),
1474        }, process_instance: u128::MAX };
1475        let fact = saddle_admission::AccountReleaseFact { account_slot: usize::MAX,
1476            account_generation: u64::MAX, audit_generation: u64::MAX, healthy: true };
1477        saddle_admission::AccountReleaseObserver::released(&observer, fact);
1478        assert_eq!(status.enqueued.load(Ordering::Relaxed), 1);
1479        // One queue slot remains occupied. Failure cannot fabricate Written.
1480        saddle_admission::AccountReleaseObserver::released(&observer, fact);
1481        assert!(status.dropped.load(Ordering::Relaxed) > 0);
1482        let DiagnosticPacket::Admitted(bytes) = receiver.recv().unwrap() else { panic!("fixed admitted frame"); };
1483        assert!(bytes.len <= 8192);
1484        let row: serde_json::Value = serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
1485        assert_eq!(row["context"]["request"]["value"], request);
1486        assert_eq!(row["account"]["account_generation"], u64::MAX);
1487        status.closed.store(true, Ordering::Release);
1488        saddle_admission::AccountReleaseObserver::released(&observer, fact);
1489        assert!(receiver.try_recv().is_err());
1490        assert_eq!(status.written.load(Ordering::Relaxed), 0);
1491    }
1492
1493    #[test]
1494    fn object_lifetime_native_frame_preserves_identity_and_queue_loss() {
1495        use saddle_core::request_context::{ContextFact, ContextLabel, RequestLocalFacts,
1496            RequestRootPublisher, RequestViewPhase};
1497        let publisher = RequestRootPublisher::create(ContextLabel::checked("objects").unwrap(),
1498            ContextFact::NotEstablished).unwrap();
1499        let view = publisher.reference().view(RequestLocalFacts::new(RequestViewPhase::Reading));
1500        let (sender, receiver) = mpsc::sync_channel(1);
1501        let status = Arc::new(Status::default());
1502        let observer = DetachedObjectLifetime {
1503            context: view.diagnostic_snapshot(), output: EmergencyDiagnosticHandle {sender, status: status.clone()},
1504            process_instance: 77, kind: "raw_body",
1505        };
1506        let fact = saddle_admission::ObjectLifetimeFact {
1507            object_id: 19, ledger_instance: 23, storage_bytes: 4096,
1508            account: saddle_admission::AccountIdentity {account_slot: 0, account_generation: 7, audit_generation: 11},
1509        };
1510        observer.emit(fact, true);
1511        observer.emit(fact, false);
1512        assert_eq!(status.dropped.load(Ordering::Relaxed), 1);
1513        let DiagnosticPacket::Admitted(bytes) = receiver.recv().unwrap() else { panic!("fixed native frame") };
1514        let row: serde_json::Value = serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
1515        assert_eq!(row["schema"], "request-object-lifetime/1");
1516        assert_eq!(row["local_request"], row["context"]["local_request"]);
1517        assert_eq!(row["ledger_instance"], 23);
1518        assert_eq!(row["account"]["audit_generation"], 11);
1519        assert_eq!(row["storage_allocated"], true);
1520        assert_eq!(row["storage_destroyed"], false);
1521        observer.emit(fact, false);
1522        let DiagnosticPacket::Admitted(bytes) = receiver.recv().unwrap() else { panic!("fixed native frame") };
1523        let released: serde_json::Value = serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
1524        assert_eq!(released["phase"], "released");
1525        assert_eq!(released["storage_destroyed"], true);
1526        assert!(released["sequence"].as_u64().unwrap() > row["sequence"].as_u64().unwrap());
1527        assert!(released["monotonic_ns"].as_u64().unwrap() >= row["monotonic_ns"].as_u64().unwrap());
1528        assert_eq!(status.written.load(Ordering::Relaxed), 0);
1529    }
1530
1531    #[test]
1532    fn source_output_requires_checked_preopen_and_keeps_selected_target() {
1533        let directory = std::env::temp_dir().join(format!(
1534            "saddle-source-output-{}-{}", std::process::id(),
1535            std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)
1536                .unwrap().as_nanos()));
1537        std::fs::create_dir(&directory).unwrap();
1538        let config = FileLoggingConfig::new(directory.clone(), crate::Rotation::Daily);
1539        let mut unchecked = EmergencyDiagnostics::start(&config).unwrap();
1540        assert!(unchecked.source_output().is_none());
1541        let deadline = std::time::Instant::now() + Duration::from_secs(5);
1542        while unchecked.shutdown() == DiagnosticShutdown::Pending
1543            && std::time::Instant::now() < deadline { std::thread::yield_now(); }
1544        assert_eq!(unchecked.shutdown(), DiagnosticShutdown::Finished);
1545        drop(unchecked);
1546
1547        let mut checked = EmergencyDiagnostics::start_checked(&config).unwrap();
1548        let selected = checked.source_output().expect("checked output");
1549        selected.handle().write_original_record(&"selected source 3701").unwrap();
1550        assert!(std::fs::read_to_string(checked.target()).unwrap()
1551            .contains("selected source 3701"));
1552        drop(selected);
1553        let deadline = std::time::Instant::now() + Duration::from_secs(5);
1554        while checked.shutdown() == DiagnosticShutdown::Pending
1555            && std::time::Instant::now() < deadline { std::thread::yield_now(); }
1556        assert_eq!(checked.shutdown(), DiagnosticShutdown::Finished);
1557        drop(checked);
1558        std::fs::remove_dir_all(directory).unwrap();
1559    }
1560
1561    #[test]
1562    fn confirmed_original_waits_for_writer_acknowledgement() {
1563        let (sender, receiver) = mpsc::sync_channel(1);
1564        let status = Arc::new(Status::default());
1565        let handle = EmergencyDiagnosticHandle { sender, status: status.clone() };
1566        // Shutdown may set closed while the worker still owns the output.
1567        // Confirmed source packets must continue through that live writer.
1568        status.closed.store(true, Ordering::Release);
1569        let (done, completed) = mpsc::sync_channel(1);
1570        let worker = thread::spawn(move || {
1571            done.send(handle.write_original_record(&"confirmed source")).unwrap();
1572        });
1573        let packet = receiver.recv().unwrap();
1574        assert!(matches!(completed.try_recv(), Err(mpsc::TryRecvError::Empty)));
1575        let DiagnosticPacket::ConfirmedOriginal(bytes, sequence) = packet else { panic!("confirmed packet"); };
1576        assert!(std::str::from_utf8(&bytes.bytes[..bytes.len]).unwrap().contains("confirmed source"));
1577        status.acknowledge(sequence, Ok(()));
1578        completed.recv().unwrap().unwrap();
1579        worker.join().unwrap();
1580
1581        let (sender, receiver) = mpsc::sync_channel(1);
1582        let status = Arc::new(Status::default());
1583        let handle = EmergencyDiagnosticHandle { sender, status: status.clone() };
1584        let worker = thread::spawn(move || handle.write_original_record(&"failed source"));
1585        let DiagnosticPacket::ConfirmedOriginal(_, sequence) = receiver.recv().unwrap() else { panic!("confirmed packet"); };
1586        status.acknowledge(sequence, Err(io::ErrorKind::PermissionDenied.into()));
1587        assert_eq!(worker.join().unwrap().unwrap_err().kind(), io::ErrorKind::PermissionDenied);
1588    }
1589
1590    #[test]
1591    fn confirmed_original_serializes_concurrent_sources_and_shutdown() {
1592        let (sender, receiver) = mpsc::sync_channel(2);
1593        let status = Arc::new(Status::default());
1594        let handle = EmergencyDiagnosticHandle { sender, status: status.clone() };
1595        let first = handle.clone();
1596        let second = handle.clone();
1597        let a = thread::spawn(move || first.write_original_record(&"source A"));
1598        let DiagnosticPacket::ConfirmedOriginal(_, first_sequence) = receiver.recv().unwrap() else { panic!("first packet"); };
1599        let b = thread::spawn(move || second.write_original_record(&"source B"));
1600        // The second caller cannot enqueue a segment until the first writer
1601        // acknowledgement releases the one process-owned confirmation slot.
1602        assert!(matches!(receiver.try_recv(), Err(mpsc::TryRecvError::Empty)));
1603        status.acknowledge(first_sequence, Ok(()));
1604        assert!(a.join().unwrap().is_ok());
1605        let DiagnosticPacket::ConfirmedOriginal(_, second_sequence) = receiver.recv_timeout(Duration::from_secs(1)).unwrap() else { panic!("second packet"); };
1606        status.acknowledge(second_sequence, Ok(()));
1607        assert!(b.join().unwrap().is_ok());
1608        status.closed.store(true, Ordering::Release);
1609        drop(receiver);
1610        status.worker_stopped.store(true, Ordering::Release);
1611        assert_eq!(handle.write_original_record(&"without a configured target").unwrap_err().kind(), io::ErrorKind::NotFound);
1612        assert_eq!(status.submitting.load(Ordering::Acquire), 0);
1613    }
1614
1615    #[test]
1616    fn cancelled_confirmation_cannot_acknowledge_next_source() {
1617        let status = Status::default();
1618        let cancelled = status.acquire_confirmation();
1619        let old_sequence = cancelled.sequence;
1620        drop(cancelled);
1621        let current = status.acquire_confirmation();
1622        status.acknowledge(old_sequence, Ok(()));
1623        assert!(status.confirmation.lock().unwrap().completed.is_none());
1624        status.acknowledge(current.sequence, Err(io::ErrorKind::PermissionDenied.into()));
1625        assert_eq!(current.wait().unwrap_err().kind(), io::ErrorKind::PermissionDenied);
1626    }
1627
1628    #[test]
1629    fn stopped_writer_releases_waiter_without_receipt() {
1630        let (sender, receiver) = mpsc::sync_channel(1);
1631        let status = Arc::new(Status::default());
1632        let weak_owner = Arc::downgrade(&status);
1633        let handle = EmergencyDiagnosticHandle { sender, status: status.clone() };
1634        let waiter = thread::spawn(move || handle.write_original_record(&"unwritten source"));
1635        let _packet = receiver.recv_timeout(Duration::from_secs(1)).unwrap();
1636        {
1637            let _state = status.confirmation.lock().unwrap();
1638            status.worker_stopped.store(true, Ordering::Release);
1639            status.confirmation_ready.notify_all();
1640        }
1641        // No selected target exists. The waiter wakes on BrokenPipe, but the
1642        // takeover cannot confirm a write and reports the missing target.
1643        assert_eq!(waiter.join().unwrap().unwrap_err().kind(), io::ErrorKind::NotFound);
1644        assert_eq!(status.snapshot().written, 0);
1645        assert_eq!(status.submitting.load(Ordering::Acquire), 0);
1646        drop(receiver);
1647        drop(status);
1648        assert!(weak_owner.upgrade().is_none(), "last owner releases the physical confirmation allocation");
1649    }
1650
1651    #[test]
1652    fn stopped_writer_with_preopened_target_writes_before_waiter_returns() {
1653        let _serial = FILE_TEST.lock().unwrap();
1654        let directory = std::env::temp_dir().join(format!(
1655            "saddle-stopped-writer-target-{}-{}", std::process::id(), sample().id()
1656        ));
1657        fs::create_dir(&directory).unwrap();
1658        let target = directory.join(EMERGENCY_FILE_NAME);
1659        let file = open_emergency_file(&target, true).unwrap();
1660        let (sender, receiver) = mpsc::sync_channel(1);
1661        let status = Arc::new(Status::default());
1662        status.fallback_path.set(BoundedTargetPath::new(&target).unwrap()).ok().unwrap();
1663        *status.fallback_file.lock().unwrap() = Some(file);
1664        let handle = EmergencyDiagnosticHandle { sender, status: status.clone() };
1665        let (done, completed) = mpsc::sync_channel(1);
1666        let waiter = thread::spawn(move || {
1667            done.send(handle.write_original_record(&"source after stopped writer 7814")).unwrap();
1668        });
1669        let DiagnosticPacket::ConfirmedOriginal(_, _) = receiver.recv_timeout(Duration::from_secs(1)).unwrap()
1670            else { panic!("confirmed packet"); };
1671        assert!(matches!(completed.try_recv(), Err(mpsc::TryRecvError::Empty)));
1672        {
1673            let _state = status.confirmation.lock().unwrap();
1674            status.worker_stopped.store(true, Ordering::Release);
1675            status.confirmation_ready.notify_all();
1676        }
1677        completed.recv_timeout(Duration::from_secs(2)).unwrap().unwrap();
1678        waiter.join().unwrap();
1679        assert_eq!(fs::read_to_string(&target).unwrap(), "\"source after stopped writer 7814\"\n");
1680        assert_eq!(status.snapshot().written, 1);
1681        assert_eq!(status.submitting.load(Ordering::Acquire), 0);
1682        drop(receiver);
1683        drop(status);
1684        fs::remove_file(target).unwrap();
1685        fs::remove_dir(directory).unwrap();
1686    }
1687
1688    #[test]
1689    fn stopped_writer_without_target_keeps_owned_original_unconfirmed() {
1690        use crate::root_diagnostic::{ComponentSourceKind, RecordedSaddleError};
1691        use saddle_core::{ContextLabel, ErrorKind};
1692        #[derive(Debug)]
1693        struct OriginalChain { cause: io::Error }
1694        impl fmt::Display for OriginalChain {
1695            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1696                formatter.write_str("unconfigured top source 7817")
1697            }
1698        }
1699        impl std::error::Error for OriginalChain {
1700            fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.cause) }
1701        }
1702        let (sender, receiver) = mpsc::sync_channel(1);
1703        drop(receiver);
1704        let status = Arc::new(Status::default());
1705        status.worker_stopped.store(true, Ordering::Release);
1706        let output = SourceOutput(EmergencyDiagnosticHandle { sender, status: status.clone() });
1707        let application = ContextLabel::checked("missing-target-test").unwrap();
1708        let result = RecordedSaddleError::component_result::<(), _>(
1709            Err(OriginalChain { cause: io::Error::other("unconfigured nested source 7818") }),
1710            &application, &output, ComponentSourceKind::Cleanup, None,
1711            ErrorKind::Infrastructure, "test.missing_target", "safe failure",
1712        );
1713        let failure = result.unwrap_err();
1714        assert_eq!(failure.safe().code(), "test.missing_target");
1715        let original = failure.original_if_unconfirmed().expect("no target cannot sign Written");
1716        assert_eq!(original.to_string(), "unconfigured top source 7817");
1717        assert_eq!(original.source().unwrap().to_string(), "unconfigured nested source 7818");
1718        assert_eq!(status.snapshot().written, 0);
1719        assert_eq!(status.submitting.load(Ordering::Acquire), 0);
1720    }
1721
1722    #[test]
1723    fn failed_writer_confirmation_retains_component_original_and_cause() {
1724        use crate::root_diagnostic::{ComponentSourceKind, RecordedSaddleError};
1725        use saddle_core::{ContextLabel, ErrorKind};
1726        #[derive(Debug)]
1727        struct OriginalChain { cause: io::Error }
1728        impl fmt::Display for OriginalChain {
1729            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1730                formatter.write_str("top source 7815")
1731            }
1732        }
1733        impl std::error::Error for OriginalChain {
1734            fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.cause) }
1735        }
1736        let (sender, receiver) = mpsc::sync_channel(1);
1737        let status = Arc::new(Status::default());
1738        let output = SourceOutput(EmergencyDiagnosticHandle { sender, status: status.clone() });
1739        let (done, completed) = mpsc::sync_channel(1);
1740        let waiter = thread::spawn(move || {
1741            let original = OriginalChain { cause: io::Error::other("nested source 7816") };
1742            let application = ContextLabel::checked("confirmation-failure-test").unwrap();
1743            let result = RecordedSaddleError::component_result::<(), _>(
1744                Err(original), &application, &output, ComponentSourceKind::Cleanup, None,
1745                ErrorKind::Infrastructure, "test.confirmation_failed", "safe failure",
1746            );
1747            done.send(result).unwrap();
1748        });
1749        let DiagnosticPacket::ConfirmedOriginal(bytes, sequence) = receiver.recv_timeout(Duration::from_secs(2)).unwrap()
1750            else { panic!("confirmed packet"); };
1751        assert!(matches!(completed.try_recv(), Err(mpsc::TryRecvError::Empty)));
1752        // Exercise the same delivery and acknowledgement path as the worker:
1753        // an actual Write error must reach the lease, not a fabricated Ok.
1754        let mut output: io::Result<Failed> = Ok(Failed);
1755        let mut stderr = Vec::new();
1756        let mut stderr_attempted = false;
1757        let delivery = deliver_with_disclosure(&mut output, &mut stderr,
1758            std::path::Path::new("/failed/controlled/source"), &bytes.bytes[..bytes.len],
1759            &status, &mut stderr_attempted, false);
1760        assert_eq!(delivery.as_ref().unwrap_err().raw_os_error(), Some(28));
1761        status.acknowledge(sequence, delivery);
1762        let failure = completed.recv_timeout(Duration::from_secs(2)).unwrap().unwrap_err();
1763        waiter.join().unwrap();
1764        assert_eq!(failure.safe().code(), "test.confirmation_failed");
1765        let original = failure.original_if_unconfirmed().expect("failed confirmation keeps owned source");
1766        assert_eq!(original.to_string(), "top source 7815");
1767        assert_eq!(original.source().unwrap().to_string(), "nested source 7816");
1768        assert_eq!(status.snapshot().written, 0);
1769        assert_eq!(status.snapshot().output_failed, 1);
1770        assert_eq!(status.submitting.load(Ordering::Acquire), 0);
1771    }
1772
1773    #[test]
1774    fn confirmed_writer_releases_process_owner_after_shutdown() {
1775        let _serial = FILE_TEST.lock().unwrap();
1776        let directory = std::env::temp_dir().join(format!(
1777            "saddle-confirm-owner-{}-{}", std::process::id(), sample().id()
1778        ));
1779        let mut owner = EmergencyDiagnostics::start(&FileLoggingConfig::new(&directory, crate::Rotation::Daily)).unwrap();
1780        let handle = owner.handle();
1781        let weak_owner = Arc::downgrade(&handle.status);
1782        handle.write_original_record(&"complete source").unwrap();
1783        let text = fs::read_to_string(owner.target()).unwrap();
1784        assert!(text.contains("complete source"));
1785        let end = std::time::Instant::now() + Duration::from_secs(3);
1786        while owner.shutdown() == DiagnosticShutdown::Pending && std::time::Instant::now() < end {
1787            thread::sleep(Duration::from_millis(1));
1788        }
1789        assert_eq!(owner.shutdown(), DiagnosticShutdown::Finished);
1790        let path = owner.target().to_path_buf();
1791        let archived = directory.join("saddle.emergency.archived.log");
1792        fs::rename(&path, &archived).unwrap();
1793        handle.write_original_record(&"after shutdown A").unwrap();
1794        handle.write_original_record(&"after shutdown B").unwrap();
1795        let archived_text = fs::read_to_string(&archived).unwrap();
1796        assert!(archived_text.contains("after shutdown A") && archived_text.contains("after shutdown B"),
1797            "the takeover pins one inode across external rotation");
1798        assert!(matches!(EmergencyDiagnostics::start(&FileLoggingConfig::new(&directory, crate::Rotation::Daily)),
1799            Err(EmergencyInitError::AlreadyActive)), "a live source handle owns the takeover target");
1800        drop(handle);
1801        drop(owner);
1802        assert!(weak_owner.upgrade().is_none());
1803        let mut restarted = EmergencyDiagnostics::start(&FileLoggingConfig::new(&directory, crate::Rotation::Daily)).unwrap();
1804        restarted.handle().write_original_record(&"new owner").unwrap();
1805        let restarted_end = std::time::Instant::now() + Duration::from_secs(3);
1806        while restarted.shutdown() == DiagnosticShutdown::Pending && std::time::Instant::now() < restarted_end {
1807            thread::sleep(Duration::from_millis(1));
1808        }
1809        assert_eq!(restarted.shutdown(), DiagnosticShutdown::Finished);
1810        drop(restarted);
1811        assert!(fs::read_to_string(&path).unwrap().contains("new owner"));
1812        fs::remove_file(path).unwrap();
1813        fs::remove_file(archived).unwrap();
1814        fs::remove_dir(directory).unwrap();
1815    }
1816
1817    #[test]
1818    fn ae_paused_queue_partial_full_disconnect_and_admitted_encoding() {
1819        use crate::{
1820            AdmissionCapacityFacts, AdmissionConstruction, AdmissionEventContext,
1821            CapacityDimension, Observer, ObserverConfig,
1822        };
1823        use saddle_admission::*;
1824        use saddle_core::*;
1825        use std::{
1826            future::Future,
1827            task::{Context, Poll, Waker},
1828        };
1829        // Real observation source, established outside the request envelope.
1830        let pending = freeze_deployment_resource_budget(
1831            2, 128, 5000, 1, 3, 1_000_000, 1_000_000, 1_000_000, 1_000_000,
1832        )
1833        .unwrap()
1834        .reserve_known_database_memory(0, 0, 0)
1835        .ok()
1836        .unwrap();
1837        let (application, listener) = BootstrapRendezvousIssuer::issue()
1838            .freeze_application(GeneratedApplicationFreezeSource::new(
1839                "app",
1840                b"descriptor",
1841                &["route"],
1842            ))
1843            .unwrap();
1844        let listener = listener
1845            .freeze_listener(ListenerStartupFreezeSource::new(
1846                "app",
1847                "127.0.0.1:8000".parse().unwrap(),
1848                "127.0.0.1:9000".parse().unwrap(),
1849                Duration::from_millis(5000),
1850            ))
1851            .ok()
1852            .unwrap();
1853        let (whole, receipt) = pair_bootstrap_rendezvous(application, listener)
1854            .ok()
1855            .unwrap();
1856        let budget = bind_deployment_resource_budget_bootstrap(pending, whole, receipt)
1857            .ok()
1858            .unwrap();
1859        let process = prepare_profusegw_lightweight_profile(budget).ok().unwrap();
1860        let (permit, original) = match process.verified_profile().try_admit_observed() {
1861            ProfuseGwLightweightObservedAdmissionOutcome::Ready(permit, receipt) => {
1862                (permit, receipt)
1863            }
1864            _ => panic!("fixture admission failed"),
1865        };
1866        let facts = AdmissionCapacityFacts::from_receipt(&original);
1867        let observer = Observer::with_writer(ObserverConfig::default(), io::sink()).unwrap();
1868        let app = ContextLabel::checked("app").unwrap();
1869        let context = AdmissionEventContext::Unrooted {
1870            application: &app,
1871            lifecycle: RequestViewPhase::Reading,
1872        };
1873        let (sender, receiver) = mpsc::sync_channel(QUEUE_CAPACITY);
1874        let handle = EmergencyDiagnosticHandle {
1875            sender,
1876            status: Arc::new(Status::default()),
1877        };
1878        for _ in 0..63 {
1879            assert_eq!(
1880                handle.submit_fixed_record(&0),
1881                DiagnosticSubmission::Enqueued
1882            );
1883        }
1884        let ledger = ProcessLedger::new(ResourceConfig {
1885            managed_capacity: 1024 * 1024,
1886            entry_reserve: 1024 * 1024,
1887            framework_reserve: 1024 * 1024,
1888            task_reserve: 1024 * 1024,
1889            process_state_reserve: 1024 * 1024,
1890            system_estimate: 1024 * 1024,
1891            safety_margin: 1024 * 1024,
1892            process_limit: 8 * 1024 * 1024,
1893            max_active_requests: 1,
1894        })
1895        .unwrap();
1896        let future = ledger
1897            .try_envelope(65536, 65536, |_| async {
1898                let result = observer.record_admission_capacity(
1899                    Some(&handle),
1900                    context,
1901                    &facts,
1902                    AdmissionConstruction::Cancelled,
1903                );
1904                assert_eq!(result.cpu, DiagnosticSubmission::Enqueued);
1905                assert_eq!(result.memory, DiagnosticSubmission::Full);
1906                assert_eq!(result.database, DiagnosticSubmission::Full);
1907                assert_eq!(result.profuse_contract, DiagnosticSubmission::Full);
1908            })
1909            .unwrap();
1910        let mut future = std::pin::pin!(future);
1911        assert!(
1912            matches!(
1913                future
1914                    .as_mut()
1915                    .poll(&mut Context::from_waker(Waker::noop())),
1916                Poll::Ready(Ok(_))
1917            ),
1918            "actual unrooted encode must not escape the request allocation envelope"
1919        );
1920        for _ in 0..63 {
1921            receiver.try_recv().unwrap();
1922        }
1923        let row: serde_json::Value =
1924            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
1925        assert_eq!(row["capacity_dimension"], "cpu");
1926        assert_eq!(row["decision"], "accepted");
1927        assert_eq!(row["resource_source"], "admission_ledger_same_lock");
1928        assert!(row["resource"]["managed_available_bytes"].as_u64().is_some());
1929        assert!(row["resource"]["framework_demand_bytes"].as_u64().is_some());
1930        assert_eq!(row["construction"]["status"], "cancelled");
1931        assert_eq!(handle.snapshot().enqueued, 64);
1932        assert_eq!(handle.snapshot().dropped, 3);
1933        // Existing encoder failure is tested independently; valid closed AE
1934        // fields cannot supply an arbitrary overlength string to force this.
1935        assert_eq!(
1936            handle.submit_fixed_record(&"x".repeat(8192)),
1937            DiagnosticSubmission::EncodingFailed
1938        );
1939        assert!(receiver.try_recv().is_err());
1940        drop(receiver);
1941        let closed = observer.record_admission_capacity(
1942            Some(&handle),
1943            context,
1944            &facts,
1945            AdmissionConstruction::Ready,
1946        );
1947        assert_eq!(
1948            [
1949                closed.cpu,
1950                closed.memory,
1951                closed.database,
1952                closed.profuse_contract
1953            ],
1954            [DiagnosticSubmission::Closed; 4]
1955        );
1956        for dimension in [
1957            CapacityDimension::Cpu,
1958            CapacityDimension::Memory,
1959            CapacityDimension::Database,
1960            CapacityDimension::ProfuseContract,
1961        ] {
1962            assert_eq!(
1963                observer
1964                    .metrics_snapshot()
1965                    .capacity_accepted(Some(dimension)),
1966                2
1967            );
1968        }
1969        assert_eq!(original.decision(), ProfuseGwCapacityDecision::Accepted);
1970        permit.into_execution().cancel();
1971        process.finish().unwrap();
1972    }
1973
1974    #[test]
1975    fn root_original_partial_cycle_full_and_disclosure_are_explicit() {
1976        use crate::root_diagnostic::OriginalCaptureState;
1977        use crate::{RootDiagnosticScope, RootOutcomeFacts, RootRequestEvent};
1978        use saddle_core::*;
1979        #[derive(Debug)]
1980        struct BadFormat;
1981        impl std::fmt::Display for BadFormat {
1982            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1983                f.write_str("exact-before-format-error")?;
1984                Err(std::fmt::Error)
1985            }
1986        }
1987        impl std::error::Error for BadFormat {}
1988        #[derive(Debug)]
1989        struct Cycle;
1990        impl std::fmt::Display for Cycle {
1991            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1992                f.write_str("cycle-original")
1993            }
1994        }
1995        impl std::error::Error for Cycle {
1996            fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1997                Some(self)
1998            }
1999        }
2000        let root = RequestRootPublisher::create(
2001            ContextLabel::checked("test").unwrap(),
2002            ContextFact::NotEstablished,
2003        )
2004        .unwrap();
2005        let view = root
2006            .reference()
2007            .view(RequestLocalFacts::new(RequestViewPhase::Handler));
2008        let (sender, receiver) = mpsc::sync_channel(64);
2009        let handle = EmergencyDiagnosticHandle {
2010            sender,
2011            status: Arc::new(Status::default()),
2012        };
2013        let source = |error: &(dyn std::error::Error + 'static), output| {
2014            RootDiagnosticScope::new(&view, output).source_error(
2015                error,
2016                DiagnosticStage::RequestHandler,
2017                RootRequestEvent::Handler,
2018                RootOutcomeFacts::default(),
2019            )
2020        };
2021        let missing = source(&BadFormat, None);
2022        assert_eq!(
2023            missing.original_capture(),
2024            OriginalCaptureState::OutputUnavailable
2025        );
2026        let partial = source(&BadFormat, Some(&handle));
2027        assert_eq!(
2028            partial.original_capture(),
2029            OriginalCaptureState::FormattingFailed
2030        );
2031        assert_eq!(partial.submission(), DiagnosticSubmission::EncodingFailed);
2032        let mut rows: Vec<serde_json::Value> = receiver
2033            .try_iter()
2034            .map(|p| serde_json::from_slice(p.bytes()).unwrap())
2035            .collect();
2036        assert_eq!(rows.last().unwrap()["payload"], "exact-before-format-error");
2037        assert_eq!(rows.last().unwrap()["state"], "formatting_failed");
2038        assert!(!rows.iter().any(|r| r["state"] == "exposed_chain_complete"));
2039        let cycle = source(&Cycle, Some(&handle));
2040        assert_eq!(cycle.original_capture(), OriginalCaptureState::CauseCycle);
2041        rows = receiver
2042            .try_iter()
2043            .map(|p| serde_json::from_slice(p.bytes()).unwrap())
2044            .collect();
2045        assert_eq!(rows.last().unwrap()["state"], "cause_cycle");
2046        // Same production queue, no retry/fallback or fabricated completion.
2047        for _ in 0..64 {
2048            assert_eq!(
2049                handle.submit_original_record(&0),
2050                DiagnosticSubmission::Enqueued
2051            );
2052        }
2053        let full = source(&Cycle, Some(&handle));
2054        assert_eq!(
2055            full.original_capture(),
2056            OriginalCaptureState::OutputRejected
2057        );
2058        assert_eq!(full.submission(), DiagnosticSubmission::Full);
2059        let _ = receiver.try_iter().count();
2060        handle.status.closed.store(true, Ordering::Release);
2061        assert_eq!(
2062            source(&Cycle, Some(&handle)).submission(),
2063            DiagnosticSubmission::Closed
2064        );
2065        // Raw original content is restricted to the existing protected file, not
2066        // mirrored into stderr if that file fails. Only output status may escape.
2067        let mut file: io::Result<Vec<u8>> = Err(io::ErrorKind::PermissionDenied.into());
2068        let mut stderr = Vec::new();
2069        let result = deliver_with_disclosure(
2070            &mut file,
2071            &mut stderr,
2072            std::path::Path::new("saddle.emergency.log"),
2073            b"CT_ARTIFICIAL_SECRET_NOT_A_CREDENTIAL",
2074            &Status::default(),
2075            &mut false,
2076            false,
2077        );
2078        assert_eq!(result.unwrap_err().kind(), io::ErrorKind::PermissionDenied);
2079        let stderr = String::from_utf8(stderr).unwrap();
2080        assert!(stderr.contains("output_failed"));
2081        assert!(!stderr.contains("CT_ARTIFICIAL_SECRET"));
2082    }
2083
2084    #[test]
2085    fn root_original_admitted_and_existing_panic_keep_identity() {
2086        use crate::root_diagnostic::OriginalCaptureState;
2087        use crate::{RootDiagnosticScope, RootOutcomeFacts, RootRequestEvent};
2088        use saddle_core::*;
2089        use std::{
2090            future::Future,
2091            task::{Context, Poll, Waker},
2092        };
2093        let root = RequestRootPublisher::create(
2094            ContextLabel::checked("test").unwrap(),
2095            ContextFact::Unavailable,
2096        )
2097        .unwrap();
2098        let view = root
2099            .reference()
2100            .view(RequestLocalFacts::new(RequestViewPhase::Handler));
2101        let (sender, receiver) = mpsc::sync_channel(64);
2102        let handle = EmergencyDiagnosticHandle {
2103            sender,
2104            status: Arc::new(Status::default()),
2105        };
2106        let diagnostic = Diagnostic::capture(
2107            DiagnosticCategory::Panic,
2108            CaptureSite::Origin,
2109            DiagnosticCause::new(
2110                DiagnosticStage::RequestHandler,
2111                DiagnosticCode::new("panic.caught").unwrap(),
2112            ),
2113        );
2114        let id = diagnostic.id();
2115        let error = io::Error::from(io::ErrorKind::TimedOut);
2116        let ledger = saddle_admission::ProcessLedger::new(saddle_admission::ResourceConfig {
2117            managed_capacity: 1024 * 1024,
2118            entry_reserve: 1024 * 1024,
2119            framework_reserve: 1024 * 1024,
2120            task_reserve: 1024 * 1024,
2121            process_state_reserve: 1024 * 1024,
2122            system_estimate: 1024 * 1024,
2123            safety_margin: 1024 * 1024,
2124            process_limit: 8 * 1024 * 1024,
2125            max_active_requests: 1,
2126        })
2127        .unwrap();
2128        let future = ledger
2129            .try_envelope(65536, 65536, |_| async {
2130                let scope = RootDiagnosticScope::new(&view, Some(&handle));
2131                let failure = scope.source_error(
2132                    &error,
2133                    DiagnosticStage::RequestDb,
2134                    RootRequestEvent::Database,
2135                    RootOutcomeFacts::default(),
2136                );
2137                assert_eq!(
2138                    failure.original_capture(),
2139                    OriginalCaptureState::CompleteEnqueued
2140                );
2141                let panic = scope.source_existing_description(
2142                    &"raw panic: CT_ARTIFICIAL_SECRET_NOT_A_CREDENTIAL",
2143                    diagnostic,
2144                    DiagnosticCode::new("task.failed").unwrap(),
2145                    RootRequestEvent::Supervision,
2146                    RootOutcomeFacts::default(),
2147                );
2148                assert_eq!(
2149                    panic.original_capture(),
2150                    OriginalCaptureState::CompleteEnqueued
2151                );
2152                panic
2153                    .finish(&view, None, RootOutcomeFacts::default())
2154                    .ok()
2155                    .unwrap()
2156            })
2157            .unwrap();
2158        let mut future = std::pin::pin!(future);
2159        assert!(
2160            matches!(
2161                future
2162                    .as_mut()
2163                    .poll(&mut Context::from_waker(Waker::noop())),
2164                Poll::Ready(Ok(_))
2165            ),
2166            "source formatter/queue must not escape admission"
2167        );
2168        let rows: Vec<serde_json::Value> = receiver
2169            .try_iter()
2170            .map(|p| serde_json::from_slice(p.bytes()).unwrap())
2171            .collect();
2172        let panic: Vec<_> = rows
2173            .iter()
2174            .filter(|r| r["occurrence"]["diagnostic_id"] == id)
2175            .collect();
2176        assert_eq!(panic.len(), 4);
2177        let header: serde_json::Value =
2178            serde_json::from_str(panic[0]["payload"].as_str().unwrap()).unwrap();
2179        assert_eq!(header["facts"]["diagnostic_id"], id);
2180        assert_eq!(header["facts"]["category"], "panic");
2181        assert_eq!(
2182            header["source_interface"],
2183            "unavailable_description_debug_only"
2184        );
2185        assert_eq!(
2186            panic[1]["payload"],
2187            "raw panic: CT_ARTIFICIAL_SECRET_NOT_A_CREDENTIAL"
2188        );
2189        assert_eq!(panic[2]["channel"], "debug");
2190        assert_eq!(panic[3]["state"], "description_complete_source_unavailable");
2191    }
2192    use saddle_core::{
2193        CaptureSite, DiagnosticCategory, DiagnosticCause, DiagnosticCode, DiagnosticStage,
2194    };
2195    #[test]
2196    fn root_contract_oversize_detail_and_basic_encoding_failure_are_explicit() {
2197        use crate::{RootDiagnosticScope, RootOutcomeFacts, RootRequestEvent};
2198        use saddle_core::*;
2199        let (sender, receiver) = mpsc::sync_channel(4);
2200        let handle = EmergencyDiagnosticHandle {
2201            sender,
2202            status: Arc::new(Status::default()),
2203        };
2204        let text = "m".repeat(256);
2205        let protocol = "\\\"".repeat(128);
2206        let call = CallContext::new(
2207            ApplicationId::new(text.clone()),
2208            ModuleId::new(text.clone()),
2209            ServiceId::new(text.clone()),
2210            OperationId::new(text.clone()),
2211            TraceId::from_u128(1),
2212            SpanId::from_u64(1),
2213        )
2214        .with_trace_correlation_id(TraceCorrelationId::new(protocol.clone()).unwrap())
2215        .with_rpc_correlation_id(RpcCorrelationId::new(&protocol));
2216        let mut root = RequestRootPublisher::create(
2217            ContextLabel::checked(&text).unwrap(),
2218            ContextFact::NotEstablished,
2219        )
2220        .unwrap();
2221        assert!(
2222            root.publish(
2223                RequestIdentityGroup::from_validated(
2224                    &call,
2225                    &protocol,
2226                    &text,
2227                    1,
2228                    ContextFact::Present(ContextLabel::checked(&text).unwrap())
2229                )
2230                .unwrap()
2231            )
2232            .is_ok()
2233        );
2234        let view = root
2235            .reference()
2236            .view(RequestLocalFacts::new(RequestViewPhase::Database));
2237        let metadata = "T".repeat(192);
2238        let cause = BoundedDiagnosticCause::new(
2239            DiagnosticStage::RequestDb,
2240            DiagnosticCode::new("driver.type").unwrap(),
2241        )
2242        .with_object(InlineDiagnosticText::metadata(&metadata))
2243        .with_types(
2244            Some(InlineDiagnosticText::metadata(&metadata)),
2245            Some(InlineDiagnosticText::metadata(&metadata)),
2246            None,
2247        );
2248        let detail = BoundedDiagnostic::capture(
2249            DiagnosticCategory::UnexpectedError,
2250            CaptureSite::Origin,
2251            cause,
2252        )
2253        .wrap(cause)
2254        .wrap(cause)
2255        .wrap(cause);
2256        let id = detail.id();
2257        let receipt = RootDiagnosticScope::new(&view, Some(&handle)).source(
2258            detail,
2259            DiagnosticCode::new("driver.type").unwrap(),
2260            RootRequestEvent::Database,
2261            RootOutcomeFacts::default(),
2262        );
2263        assert_eq!(receipt.submission(), DiagnosticSubmission::Enqueued);
2264        let row: serde_json::Value =
2265            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
2266        assert_eq!(row["detail_status"], "omitted_encoding_capacity");
2267        assert_eq!(row["occurrence"]["diagnostic_id"], id);
2268        assert!(row["diagnostic"].is_null());
2269        assert_eq!(handle.snapshot().truncated, 1);
2270        let (receipt, result) = receipt
2271            .boundary(
2272                &view,
2273                Some(&handle),
2274                RootRequestEvent::Database,
2275                RootOutcomeFacts::default(),
2276            )
2277            .ok()
2278            .unwrap();
2279        assert_eq!(result, DiagnosticSubmission::EncodingFailed);
2280        assert!(receiver.try_recv().is_err());
2281        assert_eq!(receipt.submission(), DiagnosticSubmission::Enqueued);
2282        assert_eq!(handle.snapshot().dropped, 1);
2283    }
2284
2285    #[test]
2286    fn root_contract_stage_terminal_is_single_and_admitted() {
2287        use crate::{
2288            Observer, ObserverConfig, RootDiagnosticScope, RootOutcomeFacts, RootRequestEvent,
2289            Stage, StageOutcome,
2290        };
2291        use saddle_core::*;
2292        use std::{
2293            future::Future,
2294            task::{Context, Poll, Waker},
2295        };
2296        let observer = Observer::with_writer(ObserverConfig::default(), std::io::sink()).unwrap();
2297        let root = RequestRootPublisher::create(
2298            ContextLabel::checked("app").unwrap(),
2299            ContextFact::NotEstablished,
2300        )
2301        .unwrap();
2302        let view = root
2303            .reference()
2304            .view(RequestLocalFacts::new(RequestViewPhase::Handler));
2305        let foreign = RequestRootPublisher::create(
2306            ContextLabel::checked("app").unwrap(),
2307            ContextFact::NotEstablished,
2308        )
2309        .unwrap();
2310        let foreign_view = foreign
2311            .reference()
2312            .view(RequestLocalFacts::new(RequestViewPhase::Handler));
2313        let ledger = saddle_admission::ProcessLedger::new(saddle_admission::ResourceConfig {
2314            managed_capacity: 1024 * 1024,
2315            entry_reserve: 1024 * 1024,
2316            framework_reserve: 1024 * 1024,
2317            task_reserve: 1024 * 1024,
2318            process_state_reserve: 1024 * 1024,
2319            system_estimate: 1024 * 1024,
2320            safety_margin: 1024 * 1024,
2321            process_limit: 8 * 1024 * 1024,
2322            max_active_requests: 1,
2323        })
2324        .unwrap();
2325        let future = ledger
2326            .try_envelope(65536, 65536, |_| async {
2327                let facts = |operation| RootOutcomeFacts {
2328                    axes: DiagnosticOutcomeAxes {
2329                        operation,
2330                        ..Default::default()
2331                    },
2332                    ..Default::default()
2333                };
2334                let stage = RootDiagnosticScope::new(&view, None)
2335                    .start_stage(&observer, RootRequestEvent::Response);
2336                // A technical failure cannot take the receipt-free completion path.
2337                let stage = stage
2338                    .finish_nonfailure(facts(OperationOutcome::Failed))
2339                    .err()
2340                    .unwrap();
2341                assert_eq!(
2342                    stage
2343                        .finish_nonfailure(facts(OperationOutcome::Succeeded))
2344                        .ok()
2345                        .unwrap(),
2346                    DiagnosticSubmission::OutputUnavailable
2347                );
2348                let source = RootDiagnosticScope::new(&view, None).source(
2349                    BoundedDiagnostic::capture(
2350                        DiagnosticCategory::UnexpectedError,
2351                        CaptureSite::Origin,
2352                        BoundedDiagnosticCause::new(
2353                            DiagnosticStage::RequestHandler,
2354                            DiagnosticCode::new("handler.failed").unwrap(),
2355                        ),
2356                    ),
2357                    DiagnosticCode::new("handler.failed").unwrap(),
2358                    RootRequestEvent::Handler,
2359                    facts(OperationOutcome::Failed),
2360                );
2361                let foreign_stage = RootDiagnosticScope::new(&foreign_view, None)
2362                    .start_stage(&observer, RootRequestEvent::Response);
2363                let (foreign_stage, source) = foreign_stage
2364                    .finish_failure(source, facts(OperationOutcome::Failed))
2365                    .err()
2366                    .unwrap();
2367                assert_eq!(
2368                    foreign_stage
2369                        .finish_nonfailure(facts(OperationOutcome::Rejected))
2370                        .ok()
2371                        .unwrap(),
2372                    DiagnosticSubmission::OutputUnavailable
2373                );
2374                let stage = RootDiagnosticScope::new(&view, None)
2375                    .start_stage(&observer, RootRequestEvent::Response);
2376                let (source, status) = stage
2377                    .finish_failure(source, facts(OperationOutcome::Failed))
2378                    .ok()
2379                    .unwrap();
2380                assert_eq!(status, DiagnosticSubmission::OutputUnavailable);
2381                source
2382                    .finish(&view, None, facts(OperationOutcome::Failed))
2383                    .ok()
2384                    .unwrap()
2385            })
2386            .unwrap();
2387        let mut future = std::pin::pin!(future);
2388        assert!(matches!(
2389            future
2390                .as_mut()
2391                .poll(&mut Context::from_waker(Waker::noop())),
2392            Poll::Ready(Ok(_))
2393        ));
2394        let metrics = observer.metrics_snapshot();
2395        assert_eq!(metrics.requests(StageOutcome::Success), 1);
2396        assert_eq!(metrics.requests(StageOutcome::Rejected), 1);
2397        assert_eq!(metrics.requests(StageOutcome::Failure), 1);
2398        assert_eq!(metrics.requests(StageOutcome::Cancelled), 0);
2399        assert_eq!(
2400            metrics.stage_latency(Stage::Response).iter().sum::<u64>(),
2401            3
2402        );
2403    }
2404
2405    #[test]
2406    fn root_contract_existing_source_cleanup_and_admitted_submission() {
2407        use crate::{RootDiagnosticScope, RootOutcomeFacts, RootRequestEvent};
2408        use saddle_core::*;
2409        use std::{
2410            future::Future,
2411            task::{Context, Poll, Waker},
2412        };
2413        let (sender, receiver) = mpsc::sync_channel(4);
2414        let handle = EmergencyDiagnosticHandle {
2415            sender,
2416            status: Arc::new(Status::default()),
2417        };
2418        let root = RequestRootPublisher::create(
2419            ContextLabel::checked("app").unwrap(),
2420            ContextFact::Unavailable,
2421        )
2422        .unwrap();
2423        let view = root.reference().view(
2424            RequestLocalFacts::new(RequestViewPhase::Handler).with_task(ContextFact::Present(17)),
2425        );
2426        let diagnostic = Diagnostic::capture(
2427            DiagnosticCategory::Panic,
2428            CaptureSite::Origin,
2429            DiagnosticCause::new(
2430                DiagnosticStage::RequestHandler,
2431                DiagnosticCode::new("panic.caught").unwrap(),
2432            ),
2433        );
2434        let id = diagnostic.id();
2435        let first = RootDiagnosticScope::new(&view, Some(&handle)).source_existing(
2436            diagnostic,
2437            DiagnosticCode::new("task.failed").unwrap(),
2438            RootRequestEvent::Supervision,
2439            RootOutcomeFacts::default(),
2440        );
2441        let occurrence = first.occurrence();
2442        let ledger = saddle_admission::ProcessLedger::new(saddle_admission::ResourceConfig {
2443            managed_capacity: 1024 * 1024,
2444            entry_reserve: 1024 * 1024,
2445            framework_reserve: 1024 * 1024,
2446            task_reserve: 1024 * 1024,
2447            process_state_reserve: 1024 * 1024,
2448            system_estimate: 1024 * 1024,
2449            safety_margin: 1024 * 1024,
2450            process_limit: 8 * 1024 * 1024,
2451            max_active_requests: 1,
2452        })
2453        .unwrap();
2454        let future = ledger
2455            .try_envelope(65536, 65536, |_| async {
2456                let cleanup = BoundedDiagnostic::capture(
2457                    DiagnosticCategory::UnexpectedError,
2458                    CaptureSite::Origin,
2459                    BoundedDiagnosticCause::new(
2460                        DiagnosticStage::FinalizerResource,
2461                        DiagnosticCode::new("cleanup.failed").unwrap(),
2462                    ),
2463                )
2464                .during_cleanup_occurrence(occurrence);
2465                let second = RootDiagnosticScope::new(&view, Some(&handle)).source(
2466                    cleanup,
2467                    DiagnosticCode::new("cleanup.failed").unwrap(),
2468                    RootRequestEvent::Finalization,
2469                    RootOutcomeFacts::default(),
2470                );
2471                assert_eq!(second.submission(), DiagnosticSubmission::Enqueued);
2472                first
2473                    .finish(&view, None, RootOutcomeFacts::default())
2474                    .ok()
2475                    .unwrap()
2476            })
2477            .unwrap();
2478        let mut future = std::pin::pin!(future);
2479        let result = future
2480            .as_mut()
2481            .poll(&mut Context::from_waker(Waker::noop()));
2482        assert!(
2483            matches!(result, Poll::Ready(Ok(_))),
2484            "bounded receipt path must not allocate outside admitted contract: {result:?}"
2485        );
2486        let primary: serde_json::Value =
2487            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
2488        let cleanup: serde_json::Value =
2489            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
2490        assert_eq!(primary["diagnostic"]["diagnostic_id"], id);
2491        assert_eq!(primary["category"], "panic");
2492        assert_eq!(
2493            primary["diagnostic"]["stack_status"],
2494            "unavailable_deferred"
2495        );
2496        assert_eq!(cleanup["occurrence"]["primary_diagnostic_id"], id);
2497        assert_ne!(cleanup["occurrence"]["diagnostic_id"], id);
2498        assert_eq!(cleanup["context"], primary["context"]);
2499    }
2500
2501    #[test]
2502    fn root_contract_real_file_readback_preserves_safe_facts_without_secret() {
2503        use crate::{RootDiagnosticScope, RootOutcomeFacts, RootRequestEvent};
2504        use saddle_core::*;
2505        let (sender, receiver) = mpsc::sync_channel(1);
2506        let handle = EmergencyDiagnosticHandle {
2507            sender,
2508            status: Arc::new(Status::default()),
2509        };
2510        let root = RequestRootPublisher::create(
2511            ContextLabel::checked("app").unwrap(),
2512            ContextFact::NotEstablished,
2513        )
2514        .unwrap();
2515        let view = root
2516            .reference()
2517            .view(RequestLocalFacts::new(RequestViewPhase::Reading));
2518        let cause = BoundedDiagnosticCause::new(
2519            DiagnosticStage::RequestDb,
2520            DiagnosticCode::new("db.type").unwrap(),
2521        )
2522        .with_object(InlineDiagnosticText::metadata(
2523            "mysql://SECRET_SENTINEL@host",
2524        ))
2525        .with_sqlstate("22003")
2526        .with_database_code(1264)
2527        .with_column(Some(3), Some(4))
2528        .with_types(
2529            Some(InlineDiagnosticText::metadata("i32")),
2530            Some(InlineDiagnosticText::metadata("BIGINT")),
2531            None,
2532        );
2533        let failure = RootDiagnosticScope::new(&view, Some(&handle)).source(
2534            BoundedDiagnostic::capture(
2535                DiagnosticCategory::UnexpectedError,
2536                CaptureSite::Origin,
2537                cause,
2538            ),
2539            DiagnosticCode::new("db.type").unwrap(),
2540            RootRequestEvent::Database,
2541            RootOutcomeFacts::default(),
2542        );
2543        let public = failure
2544            .finish(&view, None, RootOutcomeFacts::default())
2545            .ok()
2546            .unwrap();
2547        drop((view, root));
2548        let path = std::env::temp_dir().join(format!(
2549            "saddle-c-readback-{}-{}.jsonl",
2550            std::process::id(),
2551            std::time::SystemTime::now()
2552                .duration_since(std::time::UNIX_EPOCH)
2553                .unwrap()
2554                .as_nanos()
2555        ));
2556        let file = std::fs::OpenOptions::new()
2557            .create_new(true)
2558            .write(true)
2559            .open(&path)
2560            .unwrap();
2561        let packet = receiver.try_recv().unwrap();
2562        let mut output = Ok(file);
2563        deliver(
2564            &mut output,
2565            &mut io::sink(),
2566            &path,
2567            packet.bytes(),
2568            &handle.status,
2569            &mut false,
2570        );
2571        output.as_mut().unwrap().flush().unwrap();
2572        let raw = std::fs::read(&path).unwrap();
2573        std::fs::remove_file(&path).unwrap();
2574        assert!(!String::from_utf8_lossy(&raw).contains("SECRET_SENTINEL"));
2575        let row: serde_json::Value = serde_json::from_slice(&raw).unwrap();
2576        assert_eq!(row["diagnostic"]["causes"][0]["object"]["redacted"], true);
2577        assert_eq!(row["diagnostic"]["causes"][0]["sqlstate"]["value"], "22003");
2578        assert_eq!(row["diagnostic"]["causes"][0]["column_index"], 3);
2579        assert_eq!(handle.snapshot().written, 1);
2580        assert_eq!(public.source_submission(), DiagnosticSubmission::Enqueued);
2581        assert_eq!(
2582            public.terminal_submission(),
2583            DiagnosticSubmission::OutputUnavailable
2584        );
2585    }
2586    #[test]
2587    fn root_contract_source_boundary_supervision_and_queue_degradation() {
2588        use crate::{
2589            RootDiagnosticScope, RootOutcomeFacts, RootRequestEvent,
2590        };
2591        use saddle_core::{
2592            BoundedDiagnostic, BoundedDiagnosticCause, ContextFact, ContextLabel,
2593            RequestLocalFacts, RequestRootPublisher, RequestViewPhase,
2594        };
2595        let (sender, receiver) = mpsc::sync_channel(2);
2596        let handle = EmergencyDiagnosticHandle {
2597            sender,
2598            status: Arc::new(Status::default()),
2599        };
2600        let root = RequestRootPublisher::create(
2601            ContextLabel::checked("app").unwrap(),
2602            ContextFact::NotEstablished,
2603        )
2604        .unwrap();
2605        let view = root
2606            .reference()
2607            .view(RequestLocalFacts::new(RequestViewPhase::Reading));
2608        let make = |output| {
2609            RootDiagnosticScope::new(&view, output).source(
2610                BoundedDiagnostic::capture(
2611                    DiagnosticCategory::UnexpectedError,
2612                    CaptureSite::Origin,
2613                    BoundedDiagnosticCause::new(
2614                        DiagnosticStage::RequestDecode,
2615                        DiagnosticCode::new("read.failed").unwrap(),
2616                    ),
2617                ),
2618                DiagnosticCode::new("decode.failed").unwrap(),
2619                RootRequestEvent::Ingress,
2620                RootOutcomeFacts::default(),
2621            )
2622        };
2623        let missing = make(None);
2624        assert_eq!(
2625            missing.submission(),
2626            DiagnosticSubmission::OutputUnavailable
2627        );
2628        let receipt = make(Some(&handle));
2629        assert_eq!(receipt.submission(), DiagnosticSubmission::Enqueued);
2630        let foreign = RequestRootPublisher::create(
2631            ContextLabel::checked("app").unwrap(),
2632            ContextFact::NotEstablished,
2633        )
2634        .unwrap()
2635        .reference()
2636        .view(RequestLocalFacts::new(RequestViewPhase::Reading));
2637        let receipt = receipt
2638            .boundary(
2639                &foreign,
2640                Some(&handle),
2641                RootRequestEvent::Supervision,
2642                RootOutcomeFacts::default(),
2643            )
2644            .err()
2645            .unwrap();
2646        let public = receipt
2647            .finish(&view, Some(&handle), RootOutcomeFacts::default())
2648            .ok()
2649            .unwrap();
2650        assert_eq!(make(Some(&handle)).submission(), DiagnosticSubmission::Full);
2651        handle.status.closed.store(true, Ordering::Release);
2652        assert_eq!(
2653            make(Some(&handle)).submission(),
2654            DiagnosticSubmission::Closed
2655        );
2656        drop((missing, foreign, view, root));
2657        // These are the actual existing queue packets, still unread until all
2658        // request owners above ended. Public error is Copy and root-free.
2659        let source: serde_json::Value =
2660            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
2661        let boundary: serde_json::Value =
2662            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
2663        let public = serde_json::to_value(public).unwrap();
2664        assert_eq!(source["occurrence"], boundary["occurrence"]);
2665        assert_eq!(source["occurrence"], public["occurrence"]);
2666        assert_eq!(source["context"], boundary["source_context"]);
2667        assert_eq!(source["context"]["trace_id"]["state"], "not_established");
2668        assert!(source["diagnostic"].is_object());
2669        assert!(boundary["diagnostic"].is_null());
2670        assert!(public.get("context").is_none());
2671        assert_eq!(handle.snapshot().written, 0);
2672        assert_eq!(handle.snapshot().enqueued, 2);
2673        assert_eq!(handle.snapshot().dropped, 2);
2674    }
2675    #[test]
2676    fn outbound_child_queue_source_boundary_full_closed() {
2677        use crate::{EventContext, RequestDiagnosticScope, RequestIdentity, RouteIdentity};
2678        use saddle_core::{
2679            BoundedDiagnosticCause, CallContext, DiagnosticOutcomeAxes, RpcCorrelationId, SpanId,
2680            TraceId,
2681        };
2682        let (sender, receiver) = mpsc::sync_channel(2);
2683        let handle = EmergencyDiagnosticHandle {
2684            sender,
2685            status: Arc::new(Status::default()),
2686        };
2687        let call = |rpc: &str, span| {
2688            CallContext::new(
2689                "app".into(),
2690                "module".into(),
2691                "service".into(),
2692                "operation".into(),
2693                TraceId::from_u128(1),
2694                SpanId::from_u64(span),
2695            )
2696            .with_rpc_correlation_id(RpcCorrelationId::new(rpc))
2697        };
2698        let parent_call = call("0", 1);
2699        let child_call = call("0.1", 2);
2700        let event = EventContext::new(
2701            RequestIdentity::new("request-1").unwrap(),
2702            RouteIdentity::new("route").unwrap(),
2703            1,
2704        )
2705        .unwrap();
2706        let parent = RequestDiagnosticScope::established(&handle, &parent_call, &event);
2707        let child = parent.derive_outbound_child(&child_call, &event).unwrap();
2708        let fail = || {
2709            child.fail(
2710                7,
2711                DiagnosticCategory::UnexpectedError,
2712                BoundedDiagnosticCause::new(
2713                    DiagnosticStage::RequestDecode,
2714                    DiagnosticCode::new("outbound.failed").unwrap(),
2715                ),
2716            )
2717        };
2718        let source = fail();
2719        assert_eq!(source.submission(), DiagnosticSubmission::Enqueued);
2720        let (retained, delivery) =
2721            source.finish_boundary_retained(Some(&handle), &DiagnosticOutcomeAxes::default());
2722        assert_eq!(*retained.error(), 7);
2723        assert_eq!(
2724            delivery.boundary_submission(),
2725            DiagnosticSubmission::Enqueued
2726        );
2727        assert_eq!(fail().submission(), DiagnosticSubmission::Full);
2728        handle.status.closed.store(true, Ordering::Release);
2729        assert_eq!(fail().submission(), DiagnosticSubmission::Closed);
2730        let source: serde_json::Value =
2731            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
2732        let boundary: serde_json::Value =
2733            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
2734        assert_eq!(source["context"], boundary["context"]);
2735        assert_eq!(
2736            source["diagnostic_reference"],
2737            boundary["diagnostic_reference"]
2738        );
2739        assert_eq!(source["context"]["rpc_id"]["value"], "0.1");
2740        assert_eq!(source["context"]["span_id"]["value"], "0000000000000002");
2741        assert!(source["diagnostic"].is_object());
2742        assert!(boundary["diagnostic"].is_null());
2743        assert_eq!(handle.snapshot().written, 0);
2744    }
2745    #[test]
2746    fn early_request_source_boundary_admitted_full_closed_and_missing_output() {
2747        use crate::{DiagnosticRequestPhase, EarlyRequestContext, RequestDiagnosticScope};
2748        use saddle_core::{BoundedDiagnosticCause, DiagnosticOutcomeAxes, OperationOutcome};
2749        use std::{
2750            future::Future,
2751            task::{Context, Poll, Waker},
2752        };
2753        let (sender, receiver) = mpsc::sync_channel(2);
2754        let handle = EmergencyDiagnosticHandle {
2755            sender,
2756            status: Arc::new(Status::default()),
2757        };
2758        let known_call = saddle_core::CallContext::new(
2759            "app".into(),
2760            "module".into(),
2761            "service".into(),
2762            "operation".into(),
2763            saddle_core::TraceId::from_u128(1),
2764            saddle_core::SpanId::from_u64(2),
2765        );
2766        let known_event = crate::EventContext::new(
2767            crate::RequestIdentity::new("real-request").unwrap(),
2768            crate::RouteIdentity::new("/route").unwrap(),
2769            1,
2770        )
2771        .unwrap();
2772        let ledger = saddle_admission::ProcessLedger::new(saddle_admission::ResourceConfig {
2773            managed_capacity: 1024 * 1024,
2774            entry_reserve: 1024 * 1024,
2775            framework_reserve: 1024 * 1024,
2776            task_reserve: 1024 * 1024,
2777            process_state_reserve: 1024 * 1024,
2778            system_estimate: 1024 * 1024,
2779            safety_margin: 1024 * 1024,
2780            process_limit: 8 * 1024 * 1024,
2781            max_active_requests: 1,
2782        })
2783        .unwrap();
2784        let future = ledger
2785            .try_envelope(65536, 65536, |_| async {
2786                let axes = DiagnosticOutcomeAxes {
2787                    operation: OperationOutcome::Failed,
2788                    ..Default::default()
2789                };
2790                let cause = || {
2791                    BoundedDiagnosticCause::new(
2792                        DiagnosticStage::RequestDecode,
2793                        DiagnosticCode::new("request.read_failed").unwrap(),
2794                    )
2795                };
2796                let enriched = RequestDiagnosticScope::early(
2797                    None,
2798                    EarlyRequestContext::socket_accepted("app"),
2799                )
2800                .with_task(crate::DiagnosticTaskId::from_runtime_id("42").unwrap())
2801                .unwrap_or_else(|_| panic!("task conflict"))
2802                .bind_established(&known_call, &known_event)
2803                .unwrap_or_else(|_| panic!("context conflict"));
2804                let unchanged =
2805                    enriched
2806                        .reborrow()
2807                        .fail(22u32, DiagnosticCategory::UnexpectedError, cause());
2808                assert_eq!(
2809                    unchanged.submission(),
2810                    DiagnosticSubmission::OutputUnavailable
2811                );
2812                let scope = RequestDiagnosticScope::early(
2813                    Some(&handle),
2814                    EarlyRequestContext::socket_accepted("app"),
2815                )
2816                .with_phase(DiagnosticRequestPhase::ReadingHead);
2817                let failure = scope.fail(23u32, DiagnosticCategory::UnexpectedError, cause());
2818                let id = failure.source_diagnostic().id();
2819                assert_eq!(failure.submission(), DiagnosticSubmission::Enqueued);
2820                let (failure, delivered) = failure.finish_boundary_retained(Some(&handle), &axes);
2821                assert_eq!(
2822                    delivered.boundary_submission(),
2823                    DiagnosticSubmission::Enqueued
2824                );
2825                assert_eq!(*failure.error(), 23);
2826                let full = scope.fail(24u32, DiagnosticCategory::UnexpectedError, cause());
2827                assert_eq!(full.submission(), DiagnosticSubmission::Full);
2828                let (full, delivered) = full.finish_boundary_retained(None, &axes);
2829                assert_eq!(*full.error(), 24);
2830                assert_eq!(delivered.source_submission(), DiagnosticSubmission::Full);
2831                assert_eq!(
2832                    delivered.boundary_submission(),
2833                    DiagnosticSubmission::OutputUnavailable
2834                );
2835                handle.status.closed.store(true, Ordering::Release);
2836                let closed = scope.fail(25u32, DiagnosticCategory::UnexpectedError, cause());
2837                assert_eq!(closed.submission(), DiagnosticSubmission::Closed);
2838                let no_output = scope.with_output(None).fail(
2839                    26u32,
2840                    DiagnosticCategory::UnexpectedError,
2841                    cause(),
2842                );
2843                assert_eq!(
2844                    no_output.submission(),
2845                    DiagnosticSubmission::OutputUnavailable
2846                );
2847                assert_eq!(*no_output.error(), 26);
2848                id
2849            })
2850            .unwrap();
2851        let mut future = std::pin::pin!(future);
2852        let result = future
2853            .as_mut()
2854            .poll(&mut Context::from_waker(Waker::noop()));
2855        assert!(
2856            matches!(result, Poll::Ready(Ok(_))),
2857            "early path escaped: {result:?}"
2858        );
2859        let source: serde_json::Value =
2860            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
2861        let boundary: serde_json::Value =
2862            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
2863        assert_eq!(source["context"], boundary["context"]);
2864        assert_eq!(
2865            source["diagnostic_reference"],
2866            boundary["diagnostic_reference"]
2867        );
2868        assert_eq!(source["context"]["trace_id"]["state"], "not_established");
2869        assert_eq!(
2870            source["context"]["lifecycle"]["value"]["value"],
2871            "reading_head"
2872        );
2873        assert!(source["diagnostic"].is_object());
2874        assert!(boundary["diagnostic"].is_null());
2875        assert_eq!(boundary["axes"]["operation"], "failed");
2876        assert_eq!(handle.snapshot().written, 0); // queue submission is not delivery
2877    }
2878    use std::time::Instant;
2879    #[test]
2880    fn early_retained_reference_late_output_keeps_original_submission_and_context() {
2881        use crate::{EarlyRequestContext, RequestDiagnosticScope};
2882        use saddle_core::{BoundedDiagnostic, BoundedDiagnosticCause, DiagnosticOutcomeAxes};
2883        let (sender, receiver) = mpsc::sync_channel(2);
2884        let handle = EmergencyDiagnosticHandle {
2885            sender,
2886            status: Arc::new(Status::default()),
2887        };
2888        let scope =
2889            RequestDiagnosticScope::early(None, EarlyRequestContext::socket_accepted("app"));
2890        let diagnostic = BoundedDiagnostic::capture(
2891            DiagnosticCategory::UnexpectedError,
2892            CaptureSite::FirstObserved,
2893            BoundedDiagnosticCause::new(
2894                DiagnosticStage::RequestDecode,
2895                DiagnosticCode::new("request.read_failed").unwrap(),
2896            ),
2897        );
2898        let source = serde_json::to_value(&diagnostic).unwrap();
2899        let reference = scope.capture_required(diagnostic).into_reference();
2900        let (retained, delivery) =
2901            reference.finish_retained(Some(&handle), &DiagnosticOutcomeAxes::default());
2902        assert_eq!(
2903            delivery.source_submission(),
2904            DiagnosticSubmission::OutputUnavailable
2905        );
2906        assert_eq!(
2907            delivery.boundary_submission(),
2908            DiagnosticSubmission::Enqueued
2909        );
2910        assert_eq!(
2911            serde_json::to_value(retained.source_diagnostic()).unwrap(),
2912            source
2913        );
2914        let packet = receiver.try_recv().unwrap();
2915        let row: serde_json::Value = serde_json::from_slice(packet.bytes()).unwrap();
2916        assert_eq!(row["source_submission"], "output_unavailable");
2917        assert_eq!(row["context"]["trace_id"]["state"], "not_established");
2918        assert!(row["diagnostic"].is_null()); // late boundary is not a fabricated source retry
2919        assert!(receiver.try_recv().is_err());
2920        assert_eq!(handle.snapshot().written, 0);
2921    }
2922    static FILE_TEST: std::sync::Mutex<()> = std::sync::Mutex::new(());
2923    #[test]
2924    fn zone_projection_exact_missing_and_unsafe_are_explicit() {
2925        use crate::{
2926            DiagnosticContextMissing as Missing, DiagnosticZone, EventContext,
2927            RequestDiagnosticScope, RequestIdentity, RouteIdentity,
2928        };
2929        let (sender, receiver) = mpsc::sync_channel(2);
2930        let handle = EmergencyDiagnosticHandle {
2931            sender,
2932            status: Arc::new(Status::default()),
2933        };
2934        let call = CallContext::new(
2935            "app".into(),
2936            "module".into(),
2937            "service".into(),
2938            "query".into(),
2939            saddle_core::TraceId::from_u128(1),
2940            saddle_core::SpanId::from_u64(2),
2941        );
2942        let event = EventContext::new(
2943            RequestIdentity::new("r1").unwrap(),
2944            RouteIdentity::new("/query").unwrap(),
2945            1,
2946        )
2947        .unwrap();
2948        let cause = saddle_core::BoundedDiagnosticCause::new(
2949            DiagnosticStage::RequestDb,
2950            DiagnosticCode::new("db.decode.type").unwrap(),
2951        );
2952        for zone in ["zone-a", "杭州-zone/蓝"] {
2953            let scope = RequestDiagnosticScope::established(&handle, &call, &event)
2954                .with_zone(DiagnosticZone::from_validated_ingress(zone).unwrap())
2955                .with_zone_missing(Missing::Unavailable);
2956            let source = scope
2957                .fail((), DiagnosticCategory::UnexpectedError, cause)
2958                .into_reference();
2959            source.record(&handle, &saddle_core::DiagnosticOutcomeAxes::default());
2960            for _ in 0..2 {
2961                let row: serde_json::Value =
2962                    serde_json::from_slice(receiver.recv().unwrap().bytes()).unwrap();
2963                assert_eq!(
2964                    row["context"]["zone"],
2965                    serde_json::json!({"state":"present","value":zone})
2966                );
2967            }
2968        }
2969        for (reason, expected) in [
2970            (Missing::NotApplicable, "not_applicable"),
2971            (Missing::NotEstablished, "not_established"),
2972            (Missing::Unavailable, "unavailable"),
2973        ] {
2974            RequestDiagnosticScope::established(&handle, &call, &event)
2975                .with_zone_missing(reason)
2976                .fail((), DiagnosticCategory::UnexpectedError, cause);
2977            let row: serde_json::Value =
2978                serde_json::from_slice(receiver.recv().unwrap().bytes()).unwrap();
2979            assert_eq!(
2980                row["context"]["zone"],
2981                serde_json::json!({"state":expected})
2982            );
2983        }
2984        for invalid in [
2985            "",
2986            " ",
2987            "https://user:SECRET@host",
2988            "zone\nSECRET",
2989            "zone?token=SECRET",
2990        ] {
2991            assert!(DiagnosticZone::from_validated_ingress(invalid).is_err());
2992        }
2993        assert!(DiagnosticZone::from_validated_ingress(&"a".repeat(257)).is_err());
2994        assert!(DiagnosticZone::from_validated_ingress(&"a".repeat(256)).is_ok());
2995    }
2996    #[test]
2997    fn existing_source_receipt_keeps_stack_and_submits_once() {
2998        use crate::{EventContext, RequestDiagnosticScope, RequestIdentity, RouteIdentity};
2999        let (sender, receiver) = mpsc::sync_channel(2);
3000        let handle = EmergencyDiagnosticHandle {
3001            sender,
3002            status: Arc::new(Status::default()),
3003        };
3004        let call = CallContext::new(
3005            "app".into(),
3006            "module".into(),
3007            "service".into(),
3008            "query".into(),
3009            saddle_core::TraceId::from_u128(1),
3010            saddle_core::SpanId::from_u64(2),
3011        );
3012        let event = EventContext::new(
3013            RequestIdentity::new("request-1").unwrap(),
3014            RouteIdentity::new("/query").unwrap(),
3015            1,
3016        )
3017        .unwrap();
3018        let diagnostic = Diagnostic::capture(
3019            DiagnosticCategory::UnexpectedError,
3020            CaptureSite::FirstObserved,
3021            DiagnosticCause::new(
3022                DiagnosticStage::RequestDb,
3023                DiagnosticCode::new("db.decode.type").unwrap(),
3024            ),
3025        );
3026        let before = serde_json::to_value(&diagnostic).unwrap();
3027        let receipt = RequestDiagnosticScope::established(&handle, &call, &event)
3028            .capture_existing(diagnostic, None);
3029        assert_eq!(receipt.submission(), DiagnosticSubmission::Enqueued);
3030        let reference = receipt.into_reference();
3031        assert_eq!(
3032            serde_json::to_value(reference.source_diagnostic()).unwrap(),
3033            before
3034        );
3035        let source: serde_json::Value =
3036            serde_json::from_slice(receiver.recv().unwrap().bytes()).unwrap();
3037        assert_eq!(source["diagnostic"], before);
3038        assert!(receiver.try_recv().is_err());
3039        assert_eq!(handle.snapshot().enqueued, 1);
3040        assert_eq!(
3041            reference.record(&handle, &saddle_core::DiagnosticOutcomeAxes::default()),
3042            DiagnosticSubmission::Enqueued
3043        );
3044        let boundary: serde_json::Value =
3045            serde_json::from_slice(receiver.recv().unwrap().bytes()).unwrap();
3046        assert!(boundary["diagnostic"].is_null());
3047        assert_eq!(
3048            source["diagnostic_reference"],
3049            boundary["diagnostic_reference"]
3050        );
3051        assert_eq!(source["context"], boundary["context"]);
3052        assert_eq!(handle.snapshot().enqueued, 2);
3053        assert_eq!(handle.snapshot().written, 0);
3054    }
3055    #[test]
3056    fn controlled_request_source_preserves_error_projection_and_degradation() {
3057        use crate::{EventContext, RequestDiagnosticScope, RequestIdentity, RouteIdentity};
3058        use saddle_core::*;
3059        let (sender, receiver) = mpsc::sync_channel(2);
3060        let handle = EmergencyDiagnosticHandle {
3061            sender,
3062            status: Arc::new(Status::default()),
3063        };
3064        let call = CallContext::new(
3065            "app".into(),
3066            "module".into(),
3067            "service".into(),
3068            "query".into(),
3069            TraceId::from_u128(1),
3070            SpanId::from_u64(2),
3071        );
3072        let event = EventContext::new(
3073            RequestIdentity::new("req-1").unwrap(),
3074            RouteIdentity::new("/agent/{code}").unwrap(),
3075            2,
3076        )
3077        .unwrap();
3078        let scope = RequestDiagnosticScope::established(&handle, &call, &event);
3079        let cause = BoundedDiagnosticCause::new(
3080            DiagnosticStage::RequestDb,
3081            DiagnosticCode::new("db.decode.type").unwrap(),
3082        );
3083        let failure = scope.fail(17u32, DiagnosticCategory::UnexpectedError, cause);
3084        assert_eq!(failure.submission(), DiagnosticSubmission::Enqueued);
3085        let failure = failure.map_error(|error| (error, false));
3086        assert_eq!(failure.error(), &(17, false));
3087        assert_eq!(
3088            failure.record_boundary(&handle, &DiagnosticOutcomeAxes::default()),
3089            DiagnosticSubmission::Enqueued
3090        );
3091        let full = scope.fail(19, DiagnosticCategory::UnexpectedError, cause);
3092        assert_eq!(full.submission(), DiagnosticSubmission::Full);
3093        assert_eq!(full.error(), &19);
3094        let source: serde_json::Value =
3095            serde_json::from_slice(receiver.recv().unwrap().bytes()).unwrap();
3096        let boundary: serde_json::Value =
3097            serde_json::from_slice(receiver.recv().unwrap().bytes()).unwrap();
3098        assert_eq!(source["context"], boundary["context"]);
3099        assert_eq!(
3100            source["diagnostic_reference"],
3101            boundary["diagnostic_reference"]
3102        );
3103        assert!(boundary["diagnostic"].is_null());
3104        assert!(source["diagnostic"].is_object());
3105        let c = &source["context"];
3106        for (key, expected) in [
3107            ("application", "app"),
3108            ("module", "module"),
3109            ("service", "service"),
3110            ("operation", "query"),
3111            ("request", "req-1"),
3112            ("route", "/agent/{code}"),
3113        ] {
3114            assert_eq!(c[key]["value"]["value"], expected);
3115        }
3116        assert_eq!(c["attempt"]["value"], 2);
3117        assert_eq!(c["span_id"]["value"], "0000000000000002");
3118        assert_eq!(c["rpc_id"]["state"], "unavailable");
3119        for field in ["scope", "task", "lifecycle", "zone", "target"] {
3120            assert_eq!(c[field]["state"], "unavailable");
3121        }
3122        handle.status.closed.store(true, Ordering::Release);
3123        let closed = scope.fail(21, DiagnosticCategory::UnexpectedError, cause);
3124        assert_eq!(closed.submission(), DiagnosticSubmission::Closed);
3125        assert_eq!(closed.error(), &21);
3126        assert_eq!(handle.snapshot().written, 0);
3127        let unavailable = RequestDiagnosticScope::output_unavailable(&call, &event);
3128        let original = BoundedDiagnostic::capture(
3129            DiagnosticCategory::UnexpectedError,
3130            CaptureSite::FirstObserved,
3131            cause,
3132        );
3133        let original_id = original.id();
3134        let reference = unavailable.capture_required(original).into_reference();
3135        assert_eq!(
3136            reference.source_submission(),
3137            DiagnosticSubmission::OutputUnavailable
3138        );
3139        assert_eq!(reference.source_diagnostic().id(), original_id);
3140        assert_eq!(
3141            reference.record(&handle, &DiagnosticOutcomeAxes::default()),
3142            DiagnosticSubmission::Closed
3143        );
3144    }
3145    #[test]
3146    fn source_detail_and_boundary_reference_are_separate() {
3147        use saddle_core::*;
3148        let (sender, receiver) = mpsc::sync_channel(4);
3149        let handle = EmergencyDiagnosticHandle {
3150            sender,
3151            status: Arc::new(Status::default()),
3152        };
3153        let cause = BoundedDiagnosticCause::new(
3154            DiagnosticStage::RequestDb,
3155            DiagnosticCode::new("db.decode.type").unwrap(),
3156        );
3157        let primary = BoundedDiagnostic::capture(
3158            DiagnosticCategory::UnexpectedError,
3159            CaptureSite::FirstObserved,
3160            cause,
3161        );
3162        let cleanup = BoundedDiagnostic::capture(
3163            DiagnosticCategory::UnexpectedError,
3164            CaptureSite::FirstObserved,
3165            cause,
3166        )
3167        .during_cleanup_of(&primary);
3168        let reference = cleanup.occurrence();
3169        let axes = DiagnosticOutcomeAxes::default();
3170        assert_eq!(
3171            handle.submit_bounded(Some(&cleanup), &axes, None),
3172            DiagnosticSubmission::Enqueued
3173        );
3174        drop(cleanup);
3175        assert_eq!(
3176            handle.submit_boundary(Some(reference), &axes, None),
3177            DiagnosticSubmission::Enqueued
3178        );
3179        assert_eq!(
3180            handle.submit_boundary_parts(Some(reference), &axes, None, Some(("database", 1))),
3181            DiagnosticSubmission::Enqueued
3182        );
3183        let source: serde_json::Value =
3184            serde_json::from_slice(receiver.recv().unwrap().bytes()).unwrap();
3185        assert!(source["diagnostic"].is_object());
3186        for event in ["framework.boundary.outcome", "framework.stage.finished"] {
3187            let boundary: serde_json::Value =
3188                serde_json::from_slice(receiver.recv().unwrap().bytes()).unwrap();
3189            assert_eq!(boundary["event"], event);
3190            assert!(boundary["diagnostic"].is_null());
3191            assert_eq!(
3192                boundary["diagnostic_reference"],
3193                source["diagnostic_reference"]
3194            );
3195            assert_eq!(
3196                boundary["diagnostic_reference"]["primary_diagnostic_id"],
3197                primary.id()
3198            );
3199            assert!(!boundary.to_string().contains("db.decode.type"));
3200        }
3201        assert_eq!(handle.snapshot().written, 0);
3202    }
3203    #[test]
3204    fn deferred_unavailable_preserves_source_through_output_worker() {
3205        use saddle_core::*;
3206        let (sender, receiver) = mpsc::sync_channel(1);
3207        let handle = EmergencyDiagnosticHandle {
3208            sender,
3209            status: Arc::new(Status::default()),
3210        };
3211        let diagnostic = Diagnostic::capture(
3212            DiagnosticCategory::UnexpectedError,
3213            CaptureSite::FirstObserved,
3214            DiagnosticCause::new(
3215                DiagnosticStage::RequestDb,
3216                DiagnosticCode::new("db.decode.type").unwrap(),
3217            ),
3218        );
3219        let original = serde_json::to_value(&diagnostic).unwrap();
3220        assert_eq!(original["stack_status"], "unavailable_deferred");
3221        assert!(diagnostic.deferred_stack().is_none());
3222        assert!(original["stack"].as_str().unwrap().is_empty());
3223        assert_eq!(handle.submit(&diagnostic), DiagnosticSubmission::Enqueued);
3224        let packet = receiver.try_recv().unwrap();
3225        let queued: serde_json::Value = serde_json::from_slice(packet.bytes()).unwrap();
3226        assert_eq!(queued["diagnostic"], original);
3227        let status = handle.status.clone();
3228        let row = std::thread::spawn(move || {
3229            let mut packet = packet;
3230            packet.resolve_on_worker(&status);
3231            serde_json::from_slice::<serde_json::Value>(packet.bytes()).unwrap()
3232        })
3233        .join()
3234        .unwrap();
3235        for key in ["diagnostic_id", "primary_diagnostic_id", "origin", "causes"] {
3236            assert_eq!(row["diagnostic"][key], original[key]);
3237        }
3238        assert_eq!(row["diagnostic"]["stack_status"], "unavailable_deferred");
3239        assert!(row["diagnostic"]["stack"].as_str().unwrap().is_empty());
3240        assert_eq!(serde_json::to_value(&diagnostic).unwrap(), original);
3241    }
3242    #[test]
3243    fn request_stage_finishes_once_with_original_receipt_context() {
3244        use saddle_core::*;
3245        let (sender, receiver) = mpsc::sync_channel(4);
3246        let handle = EmergencyDiagnosticHandle {
3247            sender,
3248            status: Arc::new(Status::default()),
3249        };
3250        let observer =
3251            crate::Observer::with_writer(crate::ObserverConfig::default(), io::sink()).unwrap();
3252        let (call, _) = observer
3253            .start_external_call_checked("app", "module", "service", "query", None)
3254            .unwrap();
3255        let event = crate::EventContext::new(
3256            crate::RequestIdentity::new("original-request").unwrap(),
3257            crate::RouteIdentity::new("/query").unwrap(),
3258            1,
3259        )
3260        .unwrap();
3261        let mut scope = crate::RequestDiagnosticScope::established(&handle, call.context(), &event)
3262            .with_zone(crate::DiagnosticZone::from_validated_ingress("zone-a").unwrap())
3263            .with_db_operation(
3264                crate::DiagnosticDbOperation::from_registered("AgentByCode").unwrap(),
3265            )
3266            .with_db_operation_missing(crate::DiagnosticContextMissing::Unavailable);
3267        let reference = scope
3268            .capture_required(BoundedDiagnostic::capture(
3269                DiagnosticCategory::UnexpectedError,
3270                CaptureSite::FirstObserved,
3271                BoundedDiagnosticCause::new(
3272                    DiagnosticStage::RequestDb,
3273                    DiagnosticCode::new("db.decode.type").unwrap(),
3274                ),
3275            ))
3276            .into_reference();
3277        scope.set_db_operation(
3278            crate::DiagnosticDbOperation::from_registered("NextOperation").unwrap(),
3279        );
3280        let stage = observer.start_stage(call.context(), crate::Stage::Database, event.clone());
3281        let axes = DiagnosticOutcomeAxes {
3282            operation: OperationOutcome::Failed,
3283            physical: PhysicalDispositionFact::Returned,
3284            ..Default::default()
3285        };
3286        let delivery = stage.finish_request_failure(Some(&handle), &axes, &reference);
3287        assert_eq!(
3288            delivery.boundary_submission(),
3289            DiagnosticSubmission::Enqueued
3290        );
3291        let source: serde_json::Value =
3292            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
3293        let boundary: serde_json::Value =
3294            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
3295        assert_eq!(source["context"], boundary["context"]);
3296        assert_eq!(source["context"]["db_operation"]["value"], "AgentByCode");
3297        assert_eq!(source["context"]["operation"]["value"]["value"], "query");
3298        let next = scope.fail(
3299            (),
3300            DiagnosticCategory::ExpectedRejection,
3301            BoundedDiagnosticCause::new(
3302                DiagnosticStage::RequestDb,
3303                DiagnosticCode::new("db.next").unwrap(),
3304            ),
3305        );
3306        assert_eq!(next.submission(), DiagnosticSubmission::Enqueued);
3307        let next: serde_json::Value =
3308            serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
3309        assert_eq!(next["context"]["db_operation"]["value"], "NextOperation");
3310        for field in ["operation", "request", "scope", "zone"] {
3311            assert_eq!(next["context"][field], source["context"][field]);
3312        }
3313        assert!(crate::DiagnosticDbOperation::from_registered("SELECT * FROM private").is_none());
3314        assert!(crate::DiagnosticDbOperation::from_registered("https://secret@host").is_none());
3315        assert_eq!(
3316            source["diagnostic_reference"],
3317            boundary["diagnostic_reference"]
3318        );
3319        assert_eq!(boundary["source_submission"], "enqueued");
3320        assert_eq!(boundary["axes"]["operation"], "failed");
3321        assert!(boundary["diagnostic"].is_null());
3322        assert!(boundary["elapsed_ms"].is_u64());
3323        assert!(receiver.try_recv().is_err());
3324        let stage = observer.start_stage(call.context(), crate::Stage::Database, event);
3325        assert_eq!(
3326            stage
3327                .finish_request_failure(None, &axes, &reference)
3328                .boundary_submission(),
3329            DiagnosticSubmission::OutputUnavailable
3330        );
3331        assert!(receiver.try_recv().is_err());
3332        assert_eq!(
3333            observer
3334                .metrics_snapshot()
3335                .stage_latency(crate::Stage::Database)
3336                .iter()
3337                .sum::<u64>(),
3338            2
3339        );
3340        call.succeed();
3341        let _ = observer.shutdown();
3342    }
3343    #[test]
3344    fn nonfailure_finish_optional_output_and_no_failed_bypass() {
3345        use saddle_core::*;
3346        let (sender, receiver) = mpsc::sync_channel(2);
3347        let handle = EmergencyDiagnosticHandle {
3348            sender,
3349            status: Arc::new(Status::default()),
3350        };
3351        let observer =
3352            crate::Observer::with_writer(crate::ObserverConfig::default(), io::sink()).unwrap();
3353        let (call, _) = observer
3354            .start_external_call_checked("app", "module", "service", "query", None)
3355            .unwrap();
3356        let event = crate::EventContext::new(
3357            crate::RequestIdentity::new("r").unwrap(),
3358            crate::RouteIdentity::new("/query").unwrap(),
3359            1,
3360        )
3361        .unwrap();
3362        for operation in [OperationOutcome::Succeeded, OperationOutcome::Rejected] {
3363            let stage = observer.start_stage(call.context(), crate::Stage::Database, event.clone());
3364            let axes = DiagnosticOutcomeAxes {
3365                operation,
3366                ..Default::default()
3367            };
3368            assert!(matches!(
3369                stage.finish_nonfailure_bounded(Some(&handle), &axes),
3370                Ok(DiagnosticSubmission::Enqueued)
3371            ));
3372            let row: serde_json::Value =
3373                serde_json::from_slice(receiver.try_recv().unwrap().bytes()).unwrap();
3374            assert!(row["diagnostic_reference"].is_null());
3375            assert!(row["diagnostic"].is_null());
3376            assert!(receiver.try_recv().is_err());
3377            let stage = observer.start_stage(call.context(), crate::Stage::Database, event.clone());
3378            assert!(matches!(
3379                stage.finish_nonfailure_bounded(None, &axes),
3380                Ok(DiagnosticSubmission::OutputUnavailable)
3381            ));
3382        }
3383        for operation in [
3384            OperationOutcome::Failed,
3385            OperationOutcome::Panicked,
3386            OperationOutcome::Unknown,
3387            OperationOutcome::TimedOut,
3388            OperationOutcome::Cancelled,
3389        ] {
3390            let stage = observer.start_stage(call.context(), crate::Stage::Database, event.clone());
3391            let axes = DiagnosticOutcomeAxes {
3392                operation,
3393                ..Default::default()
3394            };
3395            let stage = match stage.finish_nonfailure_bounded(None, &axes) {
3396                Err(stage) => stage,
3397                Ok(_) => panic!("technical axis bypass"),
3398            };
3399            assert!(receiver.try_recv().is_err());
3400            let reference =
3401                crate::RequestDiagnosticScope::output_unavailable(call.context(), &event)
3402                    .capture_required(BoundedDiagnostic::capture(
3403                        DiagnosticCategory::UnexpectedError,
3404                        CaptureSite::FirstObserved,
3405                        BoundedDiagnosticCause::new(
3406                            DiagnosticStage::RequestDb,
3407                            DiagnosticCode::new("db.decode.type").unwrap(),
3408                        ),
3409                    ))
3410                    .into_reference();
3411            assert_eq!(
3412                stage
3413                    .finish_request_failure(None, &axes, &reference)
3414                    .boundary_submission(),
3415                DiagnosticSubmission::OutputUnavailable
3416            );
3417        }
3418        assert_eq!(
3419            observer
3420                .metrics_snapshot()
3421                .stage_latency(crate::Stage::Database)
3422                .iter()
3423                .sum::<u64>(),
3424            8
3425        );
3426        call.succeed();
3427        let _ = observer.shutdown();
3428    }
3429    #[test]
3430    fn bounded_diagnostic_admitted_no_escape_full_closed() {
3431        use saddle_core::*;
3432        use std::{
3433            future::Future,
3434            task::{Context, Poll, Waker},
3435        };
3436        let (sender, receiver) = mpsc::sync_channel(1);
3437        let handle = EmergencyDiagnosticHandle {
3438            sender,
3439            status: Arc::new(Status::default()),
3440        };
3441        let observer =
3442            crate::Observer::with_writer(crate::ObserverConfig::default(), io::sink()).unwrap();
3443        let (call, _) = observer
3444            .start_external_call_checked("app", "module", "service", "query", None)
3445            .unwrap();
3446        let stage = observer.start_stage(
3447            call.context(),
3448            crate::Stage::Database,
3449            crate::EventContext::new(
3450                crate::RequestIdentity::new("r1").unwrap(),
3451                crate::RouteIdentity::new("query").unwrap(),
3452                1,
3453            )
3454            .unwrap(),
3455        );
3456        let diagnostic_event = crate::EventContext::new(
3457            crate::RequestIdentity::new("r1").unwrap(),
3458            crate::RouteIdentity::new("/query").unwrap(),
3459            1,
3460        )
3461        .unwrap();
3462        let diagnostic_call = call.context().clone();
3463        let success_stage = observer.start_stage(
3464            call.context(),
3465            crate::Stage::Database,
3466            diagnostic_event.clone(),
3467        );
3468        let receipt_stage = observer.start_stage(
3469            call.context(),
3470            crate::Stage::Database,
3471            diagnostic_event.clone(),
3472        );
3473        let unavailable_stage = observer.start_stage(
3474            call.context(),
3475            crate::Stage::Database,
3476            diagnostic_event.clone(),
3477        );
3478        let ledger = saddle_admission::ProcessLedger::new(saddle_admission::ResourceConfig {
3479            managed_capacity: 1024 * 1024,
3480            entry_reserve: 1024 * 1024,
3481            framework_reserve: 1024 * 1024,
3482            task_reserve: 1024 * 1024,
3483            process_state_reserve: 1024 * 1024,
3484            system_estimate: 1024 * 1024,
3485            safety_margin: 1024 * 1024,
3486            process_limit: 8 * 1024 * 1024,
3487            max_active_requests: 1,
3488        })
3489        .unwrap();
3490        let future = ledger
3491            .try_envelope(65536, 65536, |_| async {
3492                assert!(matches!(
3493                    success_stage.finish_nonfailure_bounded(
3494                        None,
3495                        &DiagnosticOutcomeAxes {
3496                            operation: OperationOutcome::Succeeded,
3497                            ..Default::default()
3498                        }
3499                    ),
3500                    Ok(DiagnosticSubmission::OutputUnavailable)
3501                ));
3502                observer.observe_database_disposition(crate::DatabaseDisposition::Returned);
3503                let cause = BoundedDiagnosticCause::new(
3504                    DiagnosticStage::RequestDb,
3505                    DiagnosticCode::new("db.decode.type").unwrap(),
3506                )
3507                .with_column(Some(0), Some(3))
3508                .with_types(
3509                    Some(InlineDiagnosticText::metadata("Option<i64>")),
3510                    Some(InlineDiagnosticText::metadata("BIGINT")),
3511                    None,
3512                );
3513                let primary = BoundedDiagnostic::capture(
3514                    DiagnosticCategory::UnexpectedError,
3515                    CaptureSite::FirstObserved,
3516                    cause,
3517                );
3518                let axes = DiagnosticOutcomeAxes {
3519                    operation: OperationOutcome::Failed,
3520                    physical: PhysicalDispositionFact::Returned,
3521                    ..Default::default()
3522                };
3523                assert_eq!(
3524                    handle.submit_bounded(Some(&primary), &axes, None),
3525                    DiagnosticSubmission::Enqueued
3526                );
3527                assert_eq!(
3528                    handle.submit_bounded(Some(&primary), &axes, None),
3529                    DiagnosticSubmission::Full
3530                );
3531                assert_eq!(
3532                    stage.finish_bounded(&handle, &axes, Some(&primary)),
3533                    DiagnosticSubmission::Full
3534                );
3535                let controlled = crate::RequestDiagnosticScope::established(
3536                    &handle,
3537                    &diagnostic_call,
3538                    &diagnostic_event,
3539                )
3540                .with_zone(crate::DiagnosticZone::from_validated_ingress("zone-a").unwrap())
3541                .with_db_operation(
3542                    crate::DiagnosticDbOperation::from_registered("AgentByCode").unwrap(),
3543                );
3544                let failure = controlled.fail(17u32, DiagnosticCategory::UnexpectedError, cause);
3545                assert_eq!(failure.submission(), DiagnosticSubmission::Full);
3546                let (original, delivery) = failure.finish_boundary(&handle, &axes);
3547                assert_eq!(original, 17);
3548                assert_eq!(delivery.boundary_submission(), DiagnosticSubmission::Full);
3549                let reference = controlled.capture_required(primary).into_reference();
3550                let delivery =
3551                    receipt_stage.finish_request_failure(Some(&handle), &axes, &reference);
3552                assert_eq!(delivery.source_submission(), DiagnosticSubmission::Full);
3553                assert_eq!(delivery.boundary_submission(), DiagnosticSubmission::Full);
3554                let delivery = unavailable_stage.finish_request_failure(None, &axes, &reference);
3555                assert_eq!(
3556                    delivery.boundary_submission(),
3557                    DiagnosticSubmission::OutputUnavailable
3558                );
3559                handle.status.closed.store(true, Ordering::Release);
3560                assert_eq!(
3561                    handle.submit_bounded(Some(reference.source_diagnostic()), &axes, None),
3562                    DiagnosticSubmission::Closed
3563                );
3564                reference.source_diagnostic().id()
3565            })
3566            .unwrap();
3567        let mut future = std::pin::pin!(future);
3568        let result = future
3569            .as_mut()
3570            .poll(&mut Context::from_waker(Waker::noop()));
3571        assert!(
3572            matches!(result, Poll::Ready(Ok(_))),
3573            "admitted capture/submit must not escape: {result:?}"
3574        );
3575        let packet = receiver.try_recv().unwrap();
3576        let row: serde_json::Value = serde_json::from_slice(packet.bytes()).unwrap();
3577        assert_eq!(row["axes"]["operation"], "failed");
3578        assert_eq!(row["axes"]["physical"], "returned");
3579        assert_eq!(handle.snapshot().dropped, 7);
3580        assert_eq!(
3581            observer
3582                .metrics_snapshot()
3583                .database(crate::DatabaseDisposition::Returned),
3584            1
3585        );
3586        assert_eq!(
3587            observer
3588                .metrics_snapshot()
3589                .database(crate::DatabaseDisposition::Discarded),
3590            0
3591        );
3592        assert_eq!(
3593            observer
3594                .metrics_snapshot()
3595                .database(crate::DatabaseDisposition::NotUsed),
3596            0
3597        );
3598        assert_eq!(
3599            observer
3600                .metrics_snapshot()
3601                .stage_latency(crate::Stage::Database)
3602                .iter()
3603                .sum::<u64>(),
3604            4
3605        );
3606        call.succeed();
3607        let _ = observer.shutdown();
3608    }
3609    #[test]
3610    fn bounded_disconnected_and_oversized_context_are_explicit() {
3611        let (sender, receiver) = mpsc::sync_channel(1);
3612        drop(receiver);
3613        let handle = EmergencyDiagnosticHandle {
3614            sender,
3615            status: Arc::new(Status::default()),
3616        };
3617        let axes = saddle_core::DiagnosticOutcomeAxes::default();
3618        assert_eq!(
3619            handle.submit_bounded(None, &axes, None),
3620            DiagnosticSubmission::Closed
3621        );
3622        let observer =
3623            crate::Observer::with_writer(crate::ObserverConfig::default(), io::sink()).unwrap();
3624        let (call, _) = observer
3625            .start_external_call_checked("app", "module", "service", "x".repeat(257), None)
3626            .unwrap();
3627        assert_eq!(
3628            handle.submit_bounded_parts(
3629                None,
3630                &axes,
3631                Some((call.context(), "request", "route")),
3632                None
3633            ),
3634            DiagnosticSubmission::EncodingFailed
3635        );
3636        assert_eq!(handle.snapshot().written, 0);
3637        assert_eq!(handle.snapshot().dropped, 2);
3638        call.succeed();
3639        let _ = observer.shutdown();
3640    }
3641    #[test]
3642    fn diagnostic_protocol_rpc_is_not_internal_span() {
3643        let observer =
3644            crate::Observer::with_writer(crate::ObserverConfig::default(), io::sink()).unwrap();
3645        let (call, _) = observer
3646            .start_external_call_with_rpc(
3647                "test",
3648                "test",
3649                "test",
3650                "query",
3651                Some("opaque-trace"),
3652                saddle_core::RpcCorrelationId::new("0.7").unwrap(),
3653            )
3654            .unwrap();
3655        let event = crate::EventContext::new(
3656            crate::RequestIdentity::new("request-7").unwrap(),
3657            crate::RouteIdentity::new("bill.query").unwrap(),
3658            1,
3659        )
3660        .unwrap();
3661        let diagnostic = sample();
3662        let row = serde_json::to_value(Envelope::new(&diagnostic, Some((call.context(), &event))))
3663            .unwrap();
3664        assert_eq!(row["rpc_id"], "0.7");
3665        assert_eq!(row["span_id"], call.context().span_id().to_string());
3666        assert_ne!(row["rpc_id"], row["span_id"]);
3667        assert_eq!(row["trace_id"], "opaque-trace");
3668        assert_eq!(row["operation"], "query");
3669        assert_eq!(row["request"], "request-7");
3670        assert!(!row.to_string().contains("DO_NOT_LOG_SECRET"));
3671        let missing = serde_json::to_value(Envelope::new(&diagnostic, None)).unwrap();
3672        for key in [
3673            "trace_id",
3674            "rpc_id",
3675            "span_id",
3676            "operation",
3677            "request",
3678            "route",
3679        ] {
3680            assert!(missing[key].is_null());
3681        }
3682        call.succeed();
3683        let _ = observer.shutdown();
3684    }
3685    fn sample() -> Diagnostic {
3686        Diagnostic::capture(
3687            DiagnosticCategory::UnexpectedError,
3688            CaptureSite::FirstObserved,
3689            DiagnosticCause::new(
3690                DiagnosticStage::RequestDb,
3691                DiagnosticCode::new("db.connection_failed").unwrap(),
3692            )
3693            .with_io(&io::Error::other("DO_NOT_LOG_SECRET"))
3694            .with_driver_details(saddle_core::DiagnosticDriverDetails::new(
3695                Some(0),
3696                Some(4),
3697                saddle_core::DiagnosticTypeName::from_metadata("core::option::Option<i64>"),
3698                saddle_core::DiagnosticTypeName::from_metadata("BIGINT"),
3699            ))
3700            .with_input_location(
3701                saddle_core::DiagnosticInputLocation::new(Some(17), Some(3))
3702                    .with_file(
3703                        saddle_core::DiagnosticLocator::from_projection(
3704                            "mapping.json",
3705                            false,
3706                            false,
3707                        )
3708                        .unwrap(),
3709                    )
3710                    .with_column(
3711                        saddle_core::DiagnosticLocator::from_projection(
3712                            "DO_NOT_LOG_SECRET",
3713                            true,
3714                            true,
3715                        )
3716                        .unwrap(),
3717                    ),
3718            ),
3719        )
3720    }
3721    struct Failed;
3722    impl Write for Failed {
3723        fn write(&mut self, _: &[u8]) -> io::Result<usize> {
3724            Err(io::Error::from_raw_os_error(28))
3725        }
3726        fn flush(&mut self) -> io::Result<()> {
3727            Ok(())
3728        }
3729    }
3730    #[test]
3731    fn diagnostic_independent_file_after_main_failure() {
3732        let _serial = FILE_TEST.lock().unwrap();
3733        let directory = std::env::temp_dir().join(format!(
3734            "saddle-emergency-{}-{}",
3735            std::process::id(),
3736            sample().id()
3737        ));
3738        let config = FileLoggingConfig::new(&directory, crate::Rotation::Daily);
3739        let mut emergency = EmergencyDiagnostics::start(&config).unwrap();
3740        struct MarkFailure(Arc<AtomicU64>);
3741        impl Write for MarkFailure {
3742            fn write(&mut self, _: &[u8]) -> io::Result<usize> {
3743                self.0.fetch_add(1, Ordering::Release);
3744                Err(io::Error::from_raw_os_error(28))
3745            }
3746            fn flush(&mut self) -> io::Result<()> {
3747                Ok(())
3748            }
3749        }
3750        let main_failed = Arc::new(AtomicU64::new(0));
3751        let observer = crate::Observer::with_writer(
3752            crate::ObserverConfig::default(),
3753            MarkFailure(main_failed.clone()),
3754        )
3755        .unwrap();
3756        let diagnostic = sample();
3757        let (call, _) = observer
3758            .start_external_call_checked("test", "test", "test", "operation", Some("safe-trace"))
3759            .unwrap();
3760        let event = crate::EventContext::new(
3761            crate::RequestIdentity::new("request-1").unwrap(),
3762            crate::RouteIdentity::new("route.test").unwrap(),
3763            1,
3764        )
3765        .unwrap();
3766        let end = Instant::now() + Duration::from_secs(3);
3767        while main_failed.load(Ordering::Acquire) == 0 && Instant::now() < end {
3768            thread::sleep(Duration::from_millis(1));
3769        }
3770        assert!(
3771            main_failed.load(Ordering::Acquire) > 0,
3772            "primary writer failed before diagnostic submission"
3773        );
3774        assert_eq!(
3775            observer.record_diagnostic(
3776                &diagnostic,
3777                &emergency.handle(),
3778                Some((call.context(), &event))
3779            ),
3780            DiagnosticSubmission::Enqueued
3781        );
3782        let end = Instant::now() + Duration::from_secs(3);
3783        while emergency.snapshot().written != 1 && Instant::now() < end {
3784            thread::sleep(Duration::from_millis(1));
3785        }
3786        assert_eq!(emergency.snapshot().written, 1);
3787        let text = fs::read_to_string(emergency.target()).unwrap();
3788        let row: serde_json::Value = serde_json::from_str(text.trim()).unwrap();
3789        assert_eq!(row["trace_id"], "safe-trace");
3790        assert_eq!(row["request"], "request-1");
3791        assert!(row["rpc_id"].is_null());
3792        assert_eq!(row["span_id"], call.context().span_id().to_string());
3793        assert_eq!(row["operation"], "operation");
3794        assert_eq!(row["diagnostic"]["diagnostic_id"], diagnostic.id());
3795        let driver = &row["diagnostic"]["causes"][0]["driver_details"];
3796        assert_eq!(driver["column_index"], 0);
3797        assert_eq!(driver["column_count"], 4);
3798        assert_eq!(
3799            driver["target_rust_type"]["value"],
3800            "core::option::Option<i64>"
3801        );
3802        assert_eq!(driver["actual_db_type"]["value"], "BIGINT");
3803        let input = &row["diagnostic"]["causes"][0]["input_location"];
3804        assert_eq!(input["json_line"], 17);
3805        assert_eq!(input["json_column"], 3);
3806        assert_eq!(input["file"]["value"], "mapping.json");
3807        assert_eq!(input["column"]["value"], "[redacted]");
3808        assert_eq!(input["locator_truncated"], true);
3809        assert_eq!(input["locator_redacted"], true);
3810        assert!(!text.contains("DO_NOT_LOG_SECRET"));
3811        let bounded = saddle_core::BoundedDiagnostic::capture(
3812            DiagnosticCategory::UnexpectedError,
3813            CaptureSite::FirstObserved,
3814            saddle_core::BoundedDiagnosticCause::new(
3815                DiagnosticStage::RequestDb,
3816                DiagnosticCode::new("db.decode.failed").unwrap(),
3817            ),
3818        );
3819        let axes = saddle_core::DiagnosticOutcomeAxes {
3820            operation: saddle_core::OperationOutcome::Failed,
3821            physical: saddle_core::PhysicalDispositionFact::Returned,
3822            business: saddle_core::BusinessOutcome::Failure,
3823            business_code: saddle_core::RegisteredDiagnosticCode::from_registered(
3824                "INVALID_ROW",
3825                &["INVALID_ROW"],
3826            ),
3827            delivery: saddle_core::ResponseDelivery::LocalWriteComplete,
3828            cleanup: saddle_core::CleanupOutcome::Succeeded,
3829            bytes_written: Some(100),
3830        };
3831        assert_eq!(
3832            emergency.handle().submit_bounded(
3833                Some(&bounded),
3834                &axes,
3835                Some((call.context(), &event))
3836            ),
3837            DiagnosticSubmission::Enqueued
3838        );
3839        let until = Instant::now() + Duration::from_secs(3);
3840        while emergency.snapshot().written != 2 && Instant::now() < until {
3841            thread::sleep(Duration::from_millis(1));
3842        }
3843        assert_eq!(emergency.snapshot().written, 2);
3844        let content = fs::read_to_string(emergency.target()).unwrap();
3845        let second: serde_json::Value =
3846            serde_json::from_str(content.lines().nth(1).unwrap()).unwrap();
3847        assert_eq!(second["axes"]["operation"], "failed");
3848        assert_eq!(second["axes"]["physical"], "returned");
3849        assert_eq!(second["axes"]["business_code"], "INVALID_ROW");
3850        assert_eq!(second["axes"]["delivery"], "local_write_complete");
3851        assert_eq!(second["axes"]["cleanup"], "succeeded");
3852        assert_eq!(second["trace_id"], "safe-trace");
3853        let end = Instant::now() + Duration::from_secs(3);
3854        while emergency.shutdown() == DiagnosticShutdown::Pending && Instant::now() < end {
3855            thread::sleep(Duration::from_millis(1));
3856        }
3857        assert_eq!(emergency.shutdown(), DiagnosticShutdown::Finished);
3858        assert_eq!(
3859            emergency.handle().submit(&diagnostic),
3860            DiagnosticSubmission::Closed
3861        );
3862        call.succeed();
3863        let _shutdown = observer.shutdown();
3864        fs::remove_file(emergency.target()).unwrap();
3865        fs::remove_dir(directory).unwrap();
3866    }
3867    #[test]
3868    fn diagnostic_directory_failure_has_real_os_cause() {
3869        let _serial = FILE_TEST.lock().unwrap();
3870        let path = std::env::temp_dir().join(format!(
3871            "saddle-emergency-file-{}-{}",
3872            std::process::id(),
3873            sample().id()
3874        ));
3875        fs::write(&path, b"not a directory").unwrap();
3876        let mut owner =
3877            EmergencyDiagnostics::start(&FileLoggingConfig::new(&path, crate::Rotation::Daily))
3878                .unwrap();
3879        let end = Instant::now() + Duration::from_secs(3);
3880        while !owner.snapshot().initialized && Instant::now() < end {
3881            thread::sleep(Duration::from_millis(1));
3882        }
3883        let failure = owner.snapshot().first_failure.unwrap();
3884        assert_eq!(failure.stage, "initialize");
3885        assert!(failure.os_code.is_some());
3886        assert_eq!(owner.target(), path.join(EMERGENCY_FILE_NAME));
3887        while owner.shutdown() == DiagnosticShutdown::Pending && Instant::now() < end {
3888            thread::sleep(Duration::from_millis(1));
3889        }
3890        assert_eq!(owner.shutdown(), DiagnosticShutdown::Finished);
3891        fs::remove_file(path).unwrap();
3892    }
3893    #[test]
3894    fn diagnostic_stuck_worker_shutdown_never_joins_early() {
3895        let (sender, _receiver) = mpsc::sync_channel(1);
3896        let (release, wait) = mpsc::channel();
3897        let (started, reached) = mpsc::channel();
3898        let worker = thread::spawn(move || {
3899            started.send(()).unwrap();
3900            wait.recv().unwrap();
3901        });
3902        reached.recv_timeout(Duration::from_secs(2)).unwrap();
3903        let mut owner = EmergencyDiagnostics {
3904            handle: EmergencyDiagnosticHandle {
3905                sender,
3906                status: Arc::new(Status::default()),
3907            },
3908            worker: Some(worker),
3909            target: PathBuf::from("unused"),
3910            source_target_preopened: false,
3911        };
3912        assert_eq!(owner.shutdown(), DiagnosticShutdown::Pending);
3913        release.send(()).unwrap();
3914        let end = Instant::now() + Duration::from_secs(3);
3915        while owner.shutdown() == DiagnosticShutdown::Pending && Instant::now() < end {
3916            thread::yield_now();
3917        }
3918        assert_eq!(owner.shutdown(), DiagnosticShutdown::Finished);
3919    }
3920    #[test]
3921    fn diagnostic_double_failure_is_counted_without_recursion() {
3922        let status = Status::default();
3923        let mut output: io::Result<Failed> = Err(io::Error::from_raw_os_error(13));
3924        let mut stderr = Vec::new();
3925        let mut attempted = false;
3926        deliver(
3927            &mut output,
3928            &mut stderr,
3929            std::path::Path::new("/safe/logs/saddle.emergency.log"),
3930            b"{\"original\":true}\n",
3931            &status,
3932            &mut attempted,
3933        );
3934        let text = String::from_utf8(stderr).unwrap();
3935        assert!(text.contains("\"os_code\":13") && text.contains("\"original\":true"));
3936        attempted = false;
3937        deliver(
3938            &mut output,
3939            &mut Failed,
3940            std::path::Path::new("./logs"),
3941            b"{}\n",
3942            &status,
3943            &mut attempted,
3944        );
3945        for _ in 0..10 {
3946            deliver(
3947                &mut output,
3948                &mut Failed,
3949                std::path::Path::new("./logs"),
3950                b"{}\n",
3951                &status,
3952                &mut attempted,
3953            );
3954        }
3955        assert_eq!(status.snapshot().stderr_failed, 1);
3956        assert_eq!(status.snapshot().stderr_suppressed, 10);
3957        assert_eq!(status.snapshot().output_failed, 12);
3958    }
3959    #[test]
3960    fn ingress_deadline_queue_and_writer_failure_are_not_success_evidence() {
3961        let (sender, receiver) = mpsc::sync_channel(1);
3962        let status = Arc::new(Status::default());
3963        let handle = EmergencyDiagnosticHandle { sender, status: status.clone() };
3964        let observer = crate::Observer::with_writer(Default::default(), io::sink()).unwrap();
3965        let (call, _) = observer.start_external_call_with_rpc("app", "profusegw", "profusegw", "route",
3966            Some("trace"), saddle_core::RpcCorrelationId::new("0").unwrap()).unwrap();
3967        let event = crate::EventContext::new(crate::RequestIdentity::new("request-a").unwrap(),
3968            crate::RouteIdentity::new("route").unwrap(), 1).unwrap();
3969        let scope = crate::RequestDiagnosticScope::established(&handle, call.context(), &event);
3970        assert_eq!(scope.ingress_deadline_bound(12345), DiagnosticSubmission::Enqueued);
3971        assert_eq!(scope.ingress_deadline_bound(12345), DiagnosticSubmission::Full);
3972        let packet = receiver.try_recv().unwrap();
3973        let mut output = Ok(Failed);
3974        let mut attempted = false;
3975        deliver(&mut output, &mut Failed, std::path::Path::new("./logs"),
3976            packet.bytes(), &status, &mut attempted);
3977        assert_eq!(status.snapshot().written, 0);
3978        assert_eq!(status.snapshot().output_failed, 1);
3979        assert!(status.snapshot().first_failure.is_some());
3980        assert_eq!(handle.snapshot().dropped, 1);
3981    }
3982
3983    #[test]
3984    fn diagnostic_full_queue_submission_does_not_wait() {
3985        let (sender, _receiver) = mpsc::sync_channel(1);
3986        let status = Arc::new(Status::default());
3987        let handle = EmergencyDiagnosticHandle { sender, status };
3988        let sample = sample();
3989        assert_eq!(handle.submit(&sample), DiagnosticSubmission::Enqueued);
3990        assert_eq!(handle.submit(&sample), DiagnosticSubmission::Full);
3991        assert_eq!(handle.snapshot().dropped, 1);
3992        assert_eq!(handle.snapshot().written, 0);
3993    }
3994}
3995
3996#[cfg(test)]
3997mod rpc_stage_frame_tests {
3998    use super::*;
3999    #[test]
4000    fn rpc_stage_native_frame_keeps_actual_call_and_original_slot_identity_with_loss() {
4001        use saddle_core::request_context::{ContextFact, ContextLabel, RequestLocalFacts,
4002            RequestRootPublisher, RequestViewPhase};
4003        let publisher = RequestRootPublisher::create(ContextLabel::checked("rpc-stage").unwrap(),
4004            ContextFact::NotEstablished).unwrap();
4005        let view = publisher.reference().view(RequestLocalFacts::new(RequestViewPhase::Reading));
4006        let (sender, receiver) = mpsc::sync_channel(1);
4007        let status = Arc::new(Status::default());
4008        let mut call_id = [0; 256];
4009        call_id[..12].copy_from_slice(b"actual-child");
4010        let observer = DetachedRpcStageRelease { context: view.diagnostic_snapshot(),
4011            output: EmergencyDiagnosticHandle {sender, status: status.clone()},
4012            process_instance: 77, call_id, call_id_len: 12 };
4013        let fact = saddle_admission::RpcStageReleaseFact { ledger_instance: 23,
4014            account: saddle_admission::AccountIdentity {account_slot: 0, account_generation: 7, audit_generation: 11},
4015            stage_slot: 3, stage_generation: 9 };
4016        saddle_admission::RpcStageReleaseObserver::released(&observer, fact);
4017        saddle_admission::RpcStageReleaseObserver::released(&observer, fact);
4018        assert_eq!(status.dropped.load(Ordering::Relaxed), 1);
4019        let DiagnosticPacket::Admitted(bytes) = receiver.recv().unwrap() else { panic!("native frame") };
4020        let row: serde_json::Value = serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
4021        assert_eq!(row["schema"], "request-rpc-stage/1");
4022        assert_eq!(row["event"], "rpc_stage_credit_released");
4023        assert_eq!(row["call_id"], "actual-child");
4024        assert_eq!(row["stage"]["stage_generation"], 9);
4025        assert_eq!(row["stage"]["account"]["audit_generation"], 11);
4026        assert_eq!(row["local_request"], row["context"]["local_request"]);
4027        assert!(row.get("physical_driver_destroyed").is_none());
4028        assert_eq!(status.written.load(Ordering::Relaxed), 0);
4029    }
4030}
4031
4032#[cfg(test)]
4033mod request_stop_decision_tests {
4034    use super::*;
4035    #[test]
4036    fn request_stop_decision_frame_preserves_actual_branch_domain_and_rpc_clock() {
4037        use saddle_core::request_context::{ContextFact, ContextLabel, RequestLocalFacts,
4038            RequestRootPublisher, RequestViewPhase};
4039        let publisher = RequestRootPublisher::create(ContextLabel::checked("stop-decision").unwrap(),
4040            ContextFact::NotEstablished).unwrap();
4041        let view = publisher.reference().view(RequestLocalFacts::new(RequestViewPhase::Reading));
4042        let (sender, receiver) = mpsc::sync_channel(2);
4043        let status = Arc::new(Status::default());
4044        let output = EmergencyDiagnosticHandle {sender, status: status.clone()};
4045        output.record_request_stop_decision(view.diagnostic_snapshot(), "peer_disconnected");
4046        output.record_request_stop_decision(view.diagnostic_snapshot(), "shutdown-guessed");
4047        output.record_request_stop_decision(view.diagnostic_snapshot(), "original_deadline");
4048        let mut rows = Vec::new();
4049        for _ in 0..2 {
4050            let DiagnosticPacket::Admitted(bytes) = receiver.recv().unwrap() else { panic!("fixed native frame") };
4051            rows.push(serde_json::from_slice::<serde_json::Value>(&bytes.bytes[..bytes.len]).unwrap());
4052        }
4053        assert_eq!(rows[0]["cause"], "peer_disconnected");
4054        assert_eq!(rows[1]["cause"], "original_deadline");
4055        assert_eq!(rows[0]["decision_point"], "handler_select_before_dispatch_drop");
4056        assert_eq!(rows[0]["schema"], "request-stop/1");
4057        assert_eq!(rows[0]["local_request"], rows[0]["context"]["local_request"]);
4058        assert!(rows[1]["sequence"].as_u64().unwrap() > rows[0]["sequence"].as_u64().unwrap());
4059        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
4060        assert!(sequence > rows[1]["sequence"].as_u64().unwrap());
4061        assert!(monotonic_ns >= rows[1]["monotonic_ns"].as_u64().unwrap() as u128);
4062        assert!(receiver.try_recv().is_err());
4063        assert_eq!(status.dropped.load(Ordering::Relaxed), 0);
4064    }
4065}
4066
4067#[cfg(test)]
4068mod lifecycle_boundary_frame_tests {
4069    use super::*;
4070    #[test]
4071    fn lifecycle_boundary_actual_fixed_frames_share_clock_and_preserve_queue_loss() {
4072        let (sender, receiver) = mpsc::sync_channel(3);
4073        let status = Arc::new(Status::default());
4074        let output = EmergencyDiagnosticHandle { sender, status: status.clone() };
4075        for (event, phase) in [("shutdown_future_completed", 1), ("claim_cutoff", 2), ("claim_published", 2)] {
4076            output.record_request_lifecycle("boundary-app", event, phase, 1);
4077        }
4078        assert_eq!(status.dropped.load(Ordering::Relaxed), 0);
4079        output.record_request_lifecycle("boundary-app", "claim_published", 2, 1);
4080        assert_eq!(status.dropped.load(Ordering::Relaxed), 1);
4081        let mut previous = 0;
4082        for event in ["shutdown_future_completed", "claim_cutoff", "claim_published"] {
4083            let DiagnosticPacket::Admitted(bytes) = receiver.recv().unwrap() else { panic!("fixed native frame") };
4084            let row: serde_json::Value = serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
4085            assert_eq!(row["schema"], "request-lifecycle-boundary/1");
4086            assert_eq!(row["application"], "boundary-app");
4087            assert_eq!(row["event"], event);
4088            assert!(row["sequence"].as_u64().unwrap() > previous);
4089            previous = row["sequence"].as_u64().unwrap();
4090            assert_eq!(row["process_instance"], serde_json::json!(finalization_process_instance()));
4091            assert!(row.get("request_id").is_none());
4092            assert!(row.get("signal_arrival").is_none());
4093        }
4094        let (sequence, _) = rpc_lifecycle_stamp();
4095        assert!(sequence > previous);
4096        assert!(receiver.try_recv().is_err());
4097    }
4098}
4099
4100#[cfg(test)]
4101mod read_owner_frame_tests {
4102    use super::*;
4103    #[test]
4104    fn read_owner_frame_binds_unpublished_task_to_actual_scalar_peer() {
4105        use saddle_core::request_context::{ContextFact,ContextLabel,RequestLocalFacts,RequestRootPublisher,RequestViewPhase};
4106        let publisher=RequestRootPublisher::create(ContextLabel::checked("read-owner").unwrap(),ContextFact::NotEstablished).unwrap();
4107        let view=publisher.reference().view(RequestLocalFacts::new(RequestViewPhase::Reading).with_task(ContextFact::Present(9)));
4108        let (sender,receiver)=mpsc::sync_channel(1);
4109        let status=Arc::new(Status::default());
4110        let output=EmergencyDiagnosticHandle{sender,status:status.clone()};
4111        output.record_request_read_owner(view.diagnostic_snapshot(),3,
4112            saddle_admission::AccountIdentity{account_slot:0,account_generation:1,audit_generation:2},
4113            std::net::SocketAddrV4::new(std::net::Ipv4Addr::LOCALHOST,23456));
4114        let DiagnosticPacket::Admitted(bytes)=receiver.recv().unwrap() else {panic!("native fixed frame")};
4115        let row:serde_json::Value=serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
4116        assert_eq!(row["schema"],"request-read-owner/1");
4117        assert_eq!(row["event"],"recognition_read_head_entered");
4118        assert_eq!(row["context"]["publication"],0);
4119        assert_eq!(row["context"]["task"],serde_json::json!({"state":"present","value":9}));
4120        assert_eq!(row["peer_address"],serde_json::json!([127,0,0,1]));
4121        assert_eq!(row["peer_port"],23456);
4122        assert_eq!(row["local_request"],row["context"]["local_request"]);
4123        assert!(row.get("task_reclaimed").is_none());
4124        assert_eq!(status.dropped.load(Ordering::Relaxed),0);
4125    }
4126}
4127
4128#[cfg(test)]
4129mod task_owner_frame_tests {
4130    use super::*;
4131    #[test]
4132    fn owner_binding_is_scalar_creation_not_reclamation() {
4133        use saddle_core::request_context::{ContextFact,ContextLabel,RequestLocalFacts,RequestRootPublisher,RequestViewPhase};
4134        let publisher=RequestRootPublisher::create(ContextLabel::checked("task-owner").unwrap(),ContextFact::NotEstablished).unwrap();
4135        let view=publisher.reference().view(RequestLocalFacts::new(RequestViewPhase::Reading).with_task(ContextFact::Present(9)));
4136        let (sender,receiver)=mpsc::sync_channel(1);
4137        let status=Arc::new(Status::default());
4138        let output=EmergencyDiagnosticHandle{sender,status:status.clone()};
4139        output.record_request_task_owner(view.diagnostic_snapshot(),3,
4140            saddle_admission::AccountIdentity{account_slot:0,account_generation:1,audit_generation:2}, "business");
4141        let DiagnosticPacket::Admitted(bytes)=receiver.recv().unwrap() else {panic!("native frame")};
4142        let row:serde_json::Value=serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
4143        assert_eq!(row["schema"],"request-task-roster/2");
4144        assert_eq!(row["task_role"],"business");
4145        assert_eq!(row["event"],"task_owner_bound");
4146        assert_eq!(row["context"]["publication"],0);
4147        assert_eq!(row["context"]["task"],serde_json::json!({"state":"present","value":9}));
4148        assert_eq!(row["ledger_instance"],3);
4149        assert!(row.get("completion").is_none());
4150        assert_eq!(status.dropped.load(Ordering::Relaxed),0);
4151        drop(receiver);
4152        output.record_request_task_owner(view.diagnostic_snapshot(),3,
4153            saddle_admission::AccountIdentity{account_slot:0,account_generation:1,audit_generation:2}, "recognition");
4154        assert_eq!(status.dropped.load(Ordering::Relaxed),1);
4155    }
4156}
4157
4158#[cfg(test)]
4159mod database_admission_frame_tests {
4160    use super::*;
4161    #[test]
4162    fn actual_database_attempt_records_typed_decision_without_claiming_release() {
4163        use saddle_core::request_context::{ContextFact,ContextLabel,RequestLocalFacts,RequestRootPublisher,RequestViewPhase};
4164        let publisher=RequestRootPublisher::create(ContextLabel::checked("database-admission").unwrap(),ContextFact::NotEstablished).unwrap();
4165        let view=publisher.reference().view(RequestLocalFacts::new(RequestViewPhase::Database).with_task(ContextFact::Present(9)));
4166        let (sender,receiver)=mpsc::sync_channel(2);
4167        let status=Arc::new(Status::default());
4168        let output=EmergencyDiagnosticHandle{sender,status:status.clone()};
4169        let account=saddle_admission::AccountIdentity{account_slot:0,account_generation:1,audit_generation:2};
4170        output.record_request_database_admission(view.diagnostic_snapshot(),3,account,true,"accepted");
4171        output.record_request_database_admission(view.diagnostic_snapshot(),3,account,false,"db_credits_exhausted");
4172        let mut previous=0;
4173        for accepted in [true,false] {
4174            let DiagnosticPacket::Admitted(bytes)=receiver.recv().unwrap() else {panic!("native frame")};
4175            let row:serde_json::Value=serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
4176            assert_eq!(row["schema"],"request-database-admission/1");
4177            assert_eq!(row["accepted"],accepted);
4178            assert_eq!(row["reason"],if accepted {"accepted"} else {"db_credits_exhausted"});
4179            assert_eq!(row["ledger_instance"],3);
4180            let sequence=row["sequence"].as_u64().unwrap();assert!(sequence>previous);previous=sequence;
4181            assert!(row.get("physical_release").is_none());
4182        }
4183        assert_eq!(status.dropped.load(Ordering::Relaxed),0);
4184    }
4185}
4186
4187#[cfg(test)]
4188mod fifo_clock_frame_tests {
4189    use super::*;
4190    #[test]
4191    fn actual_fixed_frame_binds_effective_deadline_and_recording_point_only() {
4192        let (sender,receiver)=mpsc::sync_channel(1);
4193        let status=Arc::new(Status::default());
4194        let output=EmergencyDiagnosticHandle{sender,status:status.clone()};
4195        let before=std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos();
4196        output.record_ingress_fifo_clock(Some(saddle_core::request_context::ContextIdentity::checked("queued-R1").unwrap()),7,"removed","deadline",12345);
4197        let after=std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos();
4198        let DiagnosticPacket::Admitted(bytes)=receiver.recv().unwrap() else {panic!("native frame")};
4199        let row:serde_json::Value=serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
4200        assert_eq!(row["schema"],"ingress-fifo-clock/1");
4201        assert_eq!(row["request_id"],"queued-R1");
4202        assert_eq!(row["queue_sequence"],7);
4203        assert_eq!(row["effective_deadline_unix_ms"],12345);
4204        let recorded=row["recorded_unix_ns"].as_u64().unwrap() as u128;
4205        assert!(before<=recorded && recorded<=after);
4206        assert!(row.get("physical_removal_unix_ns").is_none());
4207        assert_eq!(status.dropped.load(Ordering::Relaxed),0);
4208    }
4209}
4210
4211#[cfg(test)]
4212mod signal_receipt_frame_tests {
4213    use super::*;
4214    #[test]
4215    fn signal_receipt_actual_frames_preserve_kind_closed_stream_and_loss() {
4216        let (sender, receiver) = mpsc::sync_channel(3);
4217        let status = Arc::new(Status::default());
4218        let output = EmergencyDiagnosticHandle { sender, status: status.clone() };
4219        let app = saddle_core::ContextLabel::checked("signal-app").unwrap();
4220        for (kind, received) in [("SIGTERM", true), ("SIGINT", true), ("SIGTERM", false)] {
4221            output.record_shutdown_signal_received(app, kind, received);
4222        }
4223        output.record_shutdown_signal_received(app, "SIGINT", false);
4224        assert_eq!(status.dropped.load(Ordering::Relaxed), 1);
4225        let mut previous = 0;
4226        for (kind, received) in [("SIGTERM", true), ("SIGINT", true), ("SIGTERM", false)] {
4227            let DiagnosticPacket::Admitted(bytes) = receiver.recv().unwrap() else { panic!("fixed frame") };
4228            let row: serde_json::Value = serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
4229            assert_eq!(row["schema"], "process-signal-receipt/1");
4230            assert_eq!(row["application"], "signal-app");
4231            assert_eq!(row["signal"], kind);
4232            assert_eq!(row["received"], received);
4233            assert_eq!(row["process_instance"], serde_json::json!(finalization_process_instance()));
4234            assert!(row["sequence"].as_u64().unwrap() > previous);
4235            previous = row["sequence"].as_u64().unwrap();
4236            assert!(row.get("kernel_arrival").is_none());
4237        }
4238        assert!(receiver.try_recv().is_err());
4239    }
4240}
4241
4242struct DetachedHandlerPoll {
4243    context: saddle_core::request_context::RequestContextSnapshot,
4244    output: EmergencyDiagnosticHandle,
4245    process_instance: u128,
4246}
4247impl saddle_admission::HandlerPollObserver for DetachedHandlerPoll {
4248    fn before_first_poll(&self) {
4249        #[derive(Serialize)]
4250        struct Record<'a> {
4251            schema: &'static str, event: &'static str, pid: u32, process_instance: u128,
4252            sequence: u64, monotonic_ns: u128,
4253            context: &'a saddle_core::request_context::RequestContextSnapshot,
4254        }
4255        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
4256        let _ = self.output.submit_fixed_record(&Record {
4257            schema: "request-handler-poll/1", event: "before_first_typed_handler_poll",
4258            pid: std::process::id(), process_instance: self.process_instance,
4259            sequence, monotonic_ns, context: &self.context,
4260        });
4261    }
4262}
4263impl EmergencyDiagnosticHandle {
4264    #[doc(hidden)]
4265    pub fn prepare_handler_poll(&self, context: saddle_core::request_context::RequestContextSnapshot,
4266        storage: &saddle_admission::ProcessLogStorage) -> Option<saddle_admission::HandlerPollObservation> {
4267        saddle_admission::HandlerPollObservation::prepare(storage, DetachedHandlerPoll {
4268            context, output: self.clone(), process_instance: finalization_process_instance(),
4269        }).ok()
4270    }
4271}
4272
4273#[cfg(test)]
4274mod handler_poll_frame_tests {
4275    use super::*;
4276    use saddle_admission::HandlerPollObserver;
4277    #[test]
4278    fn handler_poll_actual_detached_frame_has_native_context_and_bounded_loss() {
4279        use saddle_core::request_context::{ContextLabel,ContextFact,RequestRootPublisher,RequestLocalFacts,RequestViewPhase};
4280        let root=RequestRootPublisher::create(ContextLabel::checked("handler-frame-app").unwrap(),ContextFact::NotEstablished).unwrap();
4281        let view=root.reference().view(RequestLocalFacts::new(RequestViewPhase::Handler).with_task(ContextFact::Present(9)));
4282        let (sender,receiver)=mpsc::sync_channel(1);
4283        let status=Arc::new(Status::default());
4284        let observer=DetachedHandlerPoll{context:view.diagnostic_snapshot(),
4285            output:EmergencyDiagnosticHandle{sender,status:status.clone()},process_instance:finalization_process_instance()};
4286        observer.before_first_poll();
4287        observer.before_first_poll(); // Queue behavior, not the wrapper's once contract.
4288        assert_eq!(status.dropped.load(Ordering::Relaxed),1);
4289        let DiagnosticPacket::Admitted(bytes)=receiver.recv().unwrap() else {panic!("fixed frame")};
4290        let row:serde_json::Value=serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
4291        assert_eq!(row["schema"],"request-handler-poll/1");
4292        assert_eq!(row["event"],"before_first_typed_handler_poll");
4293        assert_eq!(row["context"]["application"]["value"],"handler-frame-app");
4294        assert_eq!(row["context"]["task"]["value"],9);
4295        assert_eq!(row["context"]["publication"],0);
4296        assert_eq!(row["process_instance"],serde_json::json!(finalization_process_instance()));
4297        assert!(row["sequence"].as_u64().unwrap()>0);
4298        assert!(row.get("handler_success").is_none());
4299    }
4300}
4301
4302/// Detached SQL checkout observation; never retains request/account authority.
4303#[doc(hidden)]
4304pub struct DbOwnerObservation {
4305    state: saddle_admission::DbOwnerObservationStorage<DbOwnerObservationState>,
4306}
4307struct DbOwnerObservationState {
4308    context: saddle_core::request_context::RequestContextSnapshot,
4309    output: EmergencyDiagnosticHandle,
4310    account: saddle_admission::AccountIdentity,
4311    ledger_instance: u64,
4312    owner_id: u64,
4313    finished: std::sync::atomic::AtomicBool,
4314    acquired: std::sync::atomic::AtomicBool,
4315    acknowledged: std::sync::atomic::AtomicBool,
4316    handed_off: std::sync::atomic::AtomicBool,
4317    unavailable: std::sync::atomic::AtomicBool,
4318}
4319#[doc(hidden)]
4320#[derive(Clone, Copy)]
4321pub enum DbOwnerDisposition { Returned, Discarded }
4322impl DbOwnerObservation {
4323    fn emit(&self, event: &'static str) {
4324        let state = self.state.get();
4325        if state.unavailable.load(Ordering::Acquire) { return; }
4326        #[derive(Serialize)]
4327        struct Record<'a> {
4328            schema: &'static str, pid: u32, process_instance: u128,
4329            local_request: u64, ledger_instance: u64,
4330            account: saddle_admission::AccountIdentity,
4331            context: &'a saddle_core::request_context::RequestContextSnapshot,
4332            owner_id: u64, event: &'static str, sequence: u64, monotonic_ns: u128,
4333        }
4334        let (sequence, monotonic_ns) = rpc_lifecycle_stamp();
4335        if sequence == 0 { state.unavailable.store(true, Ordering::Release); return; }
4336        let result = state.output.submit_fixed_record(&Record {
4337            schema: "request-db-owner/1", pid: std::process::id(),
4338            process_instance: finalization_process_instance(),
4339            local_request: state.context.local_request_id(), ledger_instance: state.ledger_instance,
4340            account: state.account, context: &state.context, owner_id: state.owner_id,
4341            event, sequence, monotonic_ns,
4342        });
4343        if result != DiagnosticSubmission::Enqueued { state.unavailable.store(true, Ordering::Release); }
4344    }
4345    pub fn acquired(&self) {
4346        let s=self.state.get();
4347        if !s.finished.load(Ordering::Acquire) && !s.acquired.swap(true, Ordering::AcqRel) { self.emit("acquired"); }
4348    }
4349    pub fn handoff(&self) {
4350        let s=self.state.get();
4351        if s.acquired.load(Ordering::Acquire) && !s.finished.load(Ordering::Acquire) && !s.handed_off.swap(true, Ordering::AcqRel) { self.emit("finalization_handoff"); }
4352    }
4353    pub fn commit_ack(&self) {
4354        let s=self.state.get();
4355        if s.acquired.load(Ordering::Acquire) && !s.finished.load(Ordering::Acquire) && !s.acknowledged.swap(true, Ordering::AcqRel) { self.emit("commit_ack"); }
4356    }
4357    pub fn finish(&mut self, disposition: DbOwnerDisposition) {
4358        if self.state.get().acquired.load(Ordering::Acquire) && !self.state.get().finished.swap(true, Ordering::AcqRel) {
4359            self.emit(match disposition { DbOwnerDisposition::Returned => "returned", DbOwnerDisposition::Discarded => "discarded" });
4360        }
4361    }
4362}
4363impl EmergencyDiagnosticHandle {
4364    #[doc(hidden)]
4365    pub fn prepare_db_owner_observation(&self,
4366        context: saddle_core::request_context::RequestContextSnapshot,
4367        ledger_instance: u64, account: saddle_admission::AccountIdentity,
4368        storage: &saddle_admission::ProcessLogStorage) -> Option<DbOwnerObservation> {
4369        static NEXT_OWNER: AtomicU64 = AtomicU64::new(1);
4370        let owner_id = NEXT_OWNER.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| id.checked_add(1)).ok()?;
4371        Some(DbOwnerObservation { state: saddle_admission::DbOwnerObservationStorage::prepare(storage, DbOwnerObservationState {
4372            context, output: self.clone(), account, ledger_instance, owner_id,
4373            finished: std::sync::atomic::AtomicBool::new(false),
4374            acquired: std::sync::atomic::AtomicBool::new(false),
4375            acknowledged: std::sync::atomic::AtomicBool::new(false),
4376            handed_off: std::sync::atomic::AtomicBool::new(false),
4377            unavailable: std::sync::atomic::AtomicBool::new(false),
4378        }).ok()? })
4379    }
4380}
4381
4382#[cfg(test)]
4383mod db_owner_observation_tests {
4384    use super::*;
4385    static TEST_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
4386    fn ledger() -> saddle_admission::ProfuseGwLightweightProcessOwner {
4387        use saddle_admission::{freeze_deployment_resource_budget,bind_deployment_resource_budget_bootstrap,prepare_profusegw_lightweight_profile};
4388        use saddle_core::{BootstrapRendezvousIssuer,GeneratedApplicationFreezeSource,ListenerStartupFreezeSource,pair_bootstrap_rendezvous};
4389        let pending=freeze_deployment_resource_budget(1,32,5000,1,1,1000000,1000000,1000000,1000000).unwrap().reserve_known_database_memory(0,0,0).ok().unwrap();
4390        let (application,listener)=BootstrapRendezvousIssuer::issue().freeze_application(GeneratedApplicationFreezeSource::new("db-owner",b"descriptor", &["route"])).unwrap();
4391        let listener=listener.freeze_listener(ListenerStartupFreezeSource::new("db-owner","127.0.0.1:8000".parse().unwrap(),"127.0.0.1:9000".parse().unwrap(),std::time::Duration::from_millis(5000))).ok().unwrap();
4392        let (whole,receipt)=pair_bootstrap_rendezvous(application,listener).ok().unwrap();
4393        prepare_profusegw_lightweight_profile(bind_deployment_resource_budget_bootstrap(pending,whole,receipt).ok().unwrap()).ok().unwrap()
4394    }
4395    fn context() -> saddle_core::request_context::RequestContextSnapshot {
4396        use saddle_core::request_context::*;
4397        RequestRootPublisher::create(ContextLabel::checked("db-owner").unwrap(),ContextFact::NotEstablished).unwrap()
4398            .reference().view(RequestLocalFacts::new(RequestViewPhase::Reading).with_task(ContextFact::Present(9))).diagnostic_snapshot()
4399    }
4400    fn account() -> saddle_admission::AccountIdentity {
4401        saddle_admission::AccountIdentity{account_slot:0,account_generation:1,audit_generation:2}
4402    }
4403    #[test]
4404    fn prepaid_detached_move_exact_charge_terminal_once_same_domain() {
4405        let _guard=TEST_LOCK.lock().unwrap();
4406        let ledger=ledger();let root=ledger.try_process_storage(saddle_admission::StorageDemand::separate(&[]).unwrap()).unwrap();let storage=root.release_log_storage();let before=ledger.ownership_snapshot();
4407        let (sender,receiver)=mpsc::sync_channel(8);let status=Arc::new(Status::default());
4408        let output=EmergencyDiagnosticHandle{sender,status:status.clone()};
4409        let snapshot=context();let owner=output.prepare_db_owner_observation(snapshot,17,account(),&storage).unwrap();
4410        let expected=saddle_admission::StorageDemand::embedded(std::alloc::Layout::new::<DbOwnerObservationState>(),std::alloc::Layout::new::<()>(),&[]).unwrap().bytes();
4411        assert_eq!(ledger.ownership_snapshot().process_storage_bytes-before.process_storage_bytes,expected);
4412        owner.acquired();owner.acquired();owner.commit_ack();owner.commit_ack();
4413        let mut moved=owner;moved.handoff();moved.handoff();moved.finish(DbOwnerDisposition::Returned);
4414        moved.finish(DbOwnerDisposition::Discarded);moved.commit_ack();moved.handoff();
4415        let mut previous=0;let mut owner_id=None;
4416        for event in ["acquired","commit_ack","finalization_handoff","returned"] {
4417            let DiagnosticPacket::Admitted(bytes)=receiver.recv().unwrap() else {panic!("fixed record")};
4418            let row:serde_json::Value=serde_json::from_slice(&bytes.bytes[..bytes.len]).unwrap();
4419            assert_eq!(row["schema"],"request-db-owner/1");assert_eq!(row["event"],event);
4420            assert_eq!(row["ledger_instance"],17);assert_eq!(row["account"],serde_json::to_value(account()).unwrap());
4421            assert_eq!(row["local_request"],row["context"]["local_request"]);
4422            let id=row["owner_id"].as_u64().unwrap();assert!(id>0);assert_eq!(*owner_id.get_or_insert(id),id);
4423            let sequence=row["sequence"].as_u64().unwrap();assert!(sequence>previous);previous=sequence;
4424        }
4425        assert!(rpc_lifecycle_stamp().0>previous);assert!(receiver.try_recv().is_err());
4426        drop(moved);assert_eq!(ledger.ownership_snapshot().process_storage_bytes,before.process_storage_bytes);
4427        println!("db_owner_state_layout={} handle_layout={} charged={}",std::mem::size_of::<DbOwnerObservationState>(),std::mem::size_of::<DbOwnerObservation>(),expected);
4428    }
4429    #[test]
4430    fn closed_or_full_output_and_failed_prep_are_observation_only() {
4431        let _guard=TEST_LOCK.lock().unwrap();
4432        let ledger=ledger();let root=ledger.try_process_storage(saddle_admission::StorageDemand::separate(&[]).unwrap()).unwrap();let storage=root.release_log_storage();let before=ledger.ownership_snapshot();
4433        for closed in [false,true] {
4434            let (sender,receiver)=mpsc::sync_channel(0);let status=Arc::new(Status::default());status.closed.store(closed,Ordering::Release);
4435            let output=EmergencyDiagnosticHandle{sender,status:status.clone()};
4436            let mut owner=output.prepare_db_owner_observation(context(),17,account(),&storage).unwrap();
4437            owner.acquired();owner.commit_ack();owner.handoff();owner.finish(DbOwnerDisposition::Discarded);
4438            assert!(owner.state.get().unavailable.load(Ordering::Acquire));assert_eq!(status.dropped.load(Ordering::Relaxed),1);
4439            assert!(receiver.try_recv().is_err());drop(owner);
4440        }
4441        assert_eq!(ledger.ownership_snapshot().process_storage_bytes,before.process_storage_bytes);
4442        let (sender,_receiver)=mpsc::sync_channel(1);let output=EmergencyDiagnosticHandle{sender,status:Arc::new(Status::default())};
4443        let layout=std::alloc::Layout::from_size_align(1024*1024*1024,8).unwrap();
4444        assert!(storage.try_reserve(layout).is_err());
4445        drop(root);assert!(output.prepare_db_owner_observation(context(),17,account(),&storage).is_none());
4446    }
4447}