4#include <neograph/context_store.h>
18enum class HookExecutionState : std::uint8_t {
19 Triggered, Pending, Dispatched, Succeeded, Failed, TimedOut,
22NEOGRAPH_API std::string_view to_string(HookExecutionState value)
noexcept;
26 std::string receipt_id;
27 bool outcome_known =
false;
28 bool succeeded =
false;
32struct HookExecutionReceiptData {
33 std::string invocation_id;
34 std::uint32_t attempt = 0;
35 HookExecutionState state = HookExecutionState::Failed;
43 static constexpr std::uint32_t SCHEMA_VERSION = 1;
46 [[nodiscard]]
const std::string& id()
const noexcept {
return id_; }
47 [[nodiscard]]
const HookExecutionReceiptData& data()
const noexcept {
return data_; }
48 [[nodiscard]] std::string serialize_canonical()
const;
51 HookExecutionReceiptData data_;
55struct HookOutboxEntryData {
56 HookInvocation invocation;
58 HookExecutionState state = HookExecutionState::Triggered;
59 std::uint32_t attempt_count = 0;
60 std::uint32_t max_attempts = 1;
61 std::chrono::system_clock::time_point deadline{};
62 std::uint64_t fencing_token = 0;
63 std::chrono::system_clock::time_point lease_expires_at{};
64 std::optional<HookExecutionReceipt> receipt;
70 static constexpr std::uint32_t SCHEMA_VERSION = 1;
73 [[nodiscard]]
const std::string& id()
const noexcept {
return id_; }
74 [[nodiscard]]
const HookOutboxEntryData& data()
const noexcept {
return data_; }
75 [[nodiscard]] std::string serialize_canonical()
const;
77 explicit HookOutboxEntry(HookOutboxEntryData data) : data_(std::move(data)) {}
78 HookOutboxEntryData data_;
82struct HookLease {
HookOutboxEntry entry; std::uint64_t fencing_token = 0; };
87 HookBoundaryBlocked(std::string invocation_id, HookExecutionState state, std::string message);
88 [[nodiscard]]
const std::string& invocation_id()
const noexcept {
return invocation_id_; }
89 [[nodiscard]] HookExecutionState state()
const noexcept {
return state_; }
91 std::string invocation_id_;
92 HookExecutionState state_;
102 virtual HookOutboxEntry enqueue(HookInvocation invocation, RuntimeEvent event,
103 std::uint32_t max_attempts,
104 std::chrono::system_clock::time_point deadline) = 0;
106 virtual std::optional<HookLease> claim(std::string_view invocation_id, std::string_view worker_id,
107 std::chrono::system_clock::time_point now,
108 std::chrono::milliseconds lease_duration) = 0;
109 virtual std::optional<HookOutboxEntry> settle(std::string_view invocation_id,
110 std::uint64_t fencing_token,
112 std::chrono::system_clock::time_point now) = 0;
113 virtual std::optional<HookOutboxEntry> reconcile(std::string_view invocation_id,
114 HookExecutionState expected_state,
115 std::string_view expected_head_id,
116 std::uint64_t expected_fencing_token,
118 virtual std::vector<HookOutboxEntry> pending()
const = 0;
119 virtual std::vector<HookOutboxEntry> reconciliation_required()
const = 0;
120 virtual std::optional<HookOutboxEntry> get(std::string_view invocation_id)
const = 0;
123class NEOGRAPH_API InMemoryHookJournal final :
public HookJournal {
125 InMemoryHookJournal();
126 ~InMemoryHookJournal()
override;
128 std::chrono::system_clock::time_point)
override;
130 std::optional<HookLease> claim(std::string_view, std::string_view, std::chrono::system_clock::time_point,
131 std::chrono::milliseconds)
override;
132 std::optional<HookOutboxEntry> settle(std::string_view, std::uint64_t,
134 std::chrono::system_clock::time_point)
override;
135 std::optional<HookOutboxEntry> reconcile(std::string_view, HookExecutionState,
136 std::string_view, std::uint64_t,
138 std::vector<HookOutboxEntry> pending()
const override;
139 std::vector<HookOutboxEntry> reconciliation_required()
const override;
140 std::optional<HookOutboxEntry> get(std::string_view)
const override;
143 std::unique_ptr<Impl> impl_;
149 std::vector<ContextArtifact> artifacts;
152using HookExecutionBackend = std::function<asio::awaitable<HookExecutionAttempt>(
153 const HookInvocation&,
155 std::uint32_t attempt,
158using HookArtifactPublisher = std::function<void(
159 const HookInvocation&,
161 const std::vector<ContextArtifact>&)>;
167 void publish(
const HookInvocation& invocation,
168 const RuntimeEvent& event,
169 const std::vector<ContextArtifact>& artifacts)
const;
171 std::shared_ptr<ContextStore> store_;
177 MandatoryHookRunner(std::shared_ptr<HookJournal> journal, std::shared_ptr<const HookRegistry> registry,
178 std::shared_ptr<const NativeHookExecutionAdapter> adapter,
179 std::string worker_id =
"local-hook-runner");
181 std::shared_ptr<const HookRegistry> registry,
182 HookExecutionBackend backend,
183 HookArtifactPublisher artifact_publisher = {},
184 std::string worker_id =
"hook-backend-runner");
191 asio::awaitable<void>
run_async(
const RuntimeEvent& event,
192 std::chrono::system_clock::time_point deadline,
193 std::uint32_t max_attempts = 1);
195 [[nodiscard]]
bool blocks(
const RuntimeEvent& event)
const;
199 enum class Delivery : std::uint8_t { Fresh, Delivered, InProgress, Unresolved, NotDelivered };
200 [[nodiscard]] Delivery delivery(
const RuntimeEvent& event)
const;
201 void lost_settlement(
const HookInvocation& invocation,
202 std::chrono::system_clock::time_point deadline,
HookOutboxEntry& entry,
204 std::shared_ptr<HookJournal> journal_;
205 std::shared_ptr<const HookRegistry> registry_;
206 std::shared_ptr<const NativeHookExecutionAdapter> adapter_;
207 HookExecutionBackend backend_;
208 HookArtifactPublisher artifact_publisher_;
209 std::string worker_id_;
@ Cancelled
"cancelled" — cancelled via session/cancel.
Owner-scoped idempotent publisher for HookOutput artifacts.
Typed failure of a protected runtime boundary.
Immutable durable terminal-attempt record.
Atomic durable-outbox abstraction.
Immutable snapshot of one logical invocation's durable outbox state.
Owns lifecycle-event identity and dispatch for one admitted host runtime.
Plans, journals, and executes mandatory hooks without owning lifecycle call sites.
asio::awaitable< void > run_async(const RuntimeEvent &event, std::chrono::system_clock::time_point deadline, std::uint32_t max_attempts=1)
Runs every Hook the registry plans for event.
bool blocks(const RuntimeEvent &event) const
Whether the registry plans a BlockingMandatory Hook for event.
Host-admitted, deterministic mandatory runtime hooks.
A receipt from the external effect boundary.
One transport-neutral Hook execution attempt and its context evidence.
Per-invocation context for a cooperative asynchronous tool.