Fault observation and retained responsibility

Core’s nx_fault_observe(domain, reason, file, line) is an overridable, bounded observation entry. The weak default implementation has no state, allocation, queue, OS lock or side effect; it introduces no implicit event object, registry, worker or instance RAM. Four scalar parameters avoid lending a temporary event structure to the product. Products that need recording must supply a strong implementation and their own static storage.

Terminal call paths

The maintained FreeRTOS default assert/stack-overflow hooks call the observation entry before parking. The stack hook does not dereference potentially corrupted task/name pointers. A returning observer does not allow a corrupted kernel to continue or release request, stack, TCB, IRQ or DMA borrows.

The FreeRTOS terminal path reads current masks through a read-only Arch leaf. Existing PRIMASK, FAULTMASK, or BASEPRI equal to or stronger than the kernel ceiling remains unchanged; otherwise it uses the actual port’s disable operation. This prevents a Mainline port’s direct BASEPRI store from weakening an existing mask. Baseline/POSIX builds still follow their own ports and do not claim MCU IRQs on the host. This is a single-core terminal contract; the observer must preserve incoming masks.

nx_freertos_assert_failed and the stack hook can still be strongly overridden. Overriding them replaces default observation, so the product must explicitly retain the nx_fault_observe call and guarantee non-returning behavior. Public declarations retain the C11/C++ ABI; returning to a corrupted configASSERT caller is outside the permitted behavior.

ThreadX observes a central adapter fail-stop or M4 external timer setup failure before invoking the product fail-stop port. If that port returns, the adapter retains responsibility and parks; it does not pretend that kernel startup, task borrows or clock effects have been undone. Support remains defined by the ThreadX document and actual targets and does not inherit every FreeRTOS CPU combination.

Product contract and limits

The observer runs in the original task, ISR or interrupt-masked context; privilege and context are not normalized. It must be bounded and allocation-free, acquire no OS locks, enter no provider, alter no accepted request, release no retained storage, and assume neither kernel, clock, stack, transport nor peripheral still works. The filename is only a static source literal or NULL; unknown dynamic memory must not be dereferenced. Observation on a corrupted stack is best effort.

The kernel ceiling does not prove global quiescence of all interrupts and does not revoke NMI, DMA or external electrical effects. This API implements no safe output, reset, Flash persistence, loan reclamation or safety certification. A product may implement separately reviewed non-returning recovery; responsibility remains in the external project.

nexus-examples/apps/uart_owner_runtime/fault_policy.* demonstrates a caller-owned first-fault record and an explicit product output port. It release-publishes diagnostics before calling the port; nested or repeated reports cannot repeat output actions or replace the first fault. A missing or failed port remains unverified, with no cleanup/reset/reclaim. The example selects no GPIO or assumed board-safe level by default. A successful software callback does not prove electrical safety; product/Board/HIL acceptance remains required.

Google death contracts verify the default hook’s observation→mask→park order, no dereference of bad task/name, loan retention when an observer returns, and strong/equal/weak BASEPRI, PRIMASK and FAULTMASK. They use the actual default production hooks and explicit port/Arch leaf models; recordability after real faults, physical safe outputs and worst-case time have no board qualification.