mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-03 08:06:01 +08:00
* feat(notifications): publish authoritative run outcomes
* fix(notifications): route run outcomes through the shared inbox seam
The outcome observer built its lifecycle references as raw strings, which no
longer typechecks now that the source carries a validated `LifecycleRef`, and
it published external-delivery failures through a second publisher with its
own id format — two mints for one `run:{id}:{kind}` namespace, so one fact
could have produced two inbox rows once the kinds overlapped. Lifecycle
references now go through a fallible helper that propagates its cause, and the
delivery-failure path calls the gate publisher's `publish_inbox_notification`,
leaving a single seam and a single id source.
* fix(notifications): close the block a delivery timeout leaves open
* test(notifications): cover the untested outcome arms and replayed identities
The observer's tests reached only the completed and failed arms, so the
recovery-required arm and both eligibility exclusions were unpinned: an edit to
either predicate would have started publishing for child or ownerless runs with
nothing failing. Cases now drive a recovery-required commit and screened
snapshots through `observe_process_commit`.
The restart leg asserted a notification count, which survives an observer that
re-mints every id, so it now compares the identity set across the restart —
identities are what deduplicate a replayed commit. The swallowed metadata
decode also carries the marker the fail-loud rule asks for, naming why an
unreadable envelope is a screening result rather than a failure to report.
Composition's absolute mass ceiling moves to the measured count. The 152 lines
this stack adds are all service-graph assembly with their behaviour in owning
crates, the stack's own tests already live in separate files, and the large
inline test modules left in composition sit in unrelated trees where splitting
one inside a notification change would dwarf its diff.
* fix(notifications): state the bound at the outcome observer's inbox
The inbox store now takes its record bound from the constructing caller, so the
outcome observer's harness states the production bound like the rest of the
callers.
* fix(notifications): retain outcome commits until replies arrive
* fix(notifications): bound stalled outcome replay