diff --git a/docs/events/projections/async-daemon.md b/docs/events/projections/async-daemon.md
index 1963ce7127..ba02bf4156 100644
--- a/docs/events/projections/async-daemon.md
+++ b/docs/events/projections/async-daemon.md
@@ -499,6 +499,33 @@ using multi-tenancy through a database per tenant. On these spans will be these
There is also a counter metric called `marten.daemon.skipping` or `marten.[database name].daemon.skipping`
that just emits and update every time that Marten has to "skip" stale events.
+## Extended Progression Tracking
+
+When you opt into extended progression tracking, Marten adds six monitoring
+columns (`heartbeat`, `agent_status`, `pause_reason`, `running_on_node`,
+`warning_behind_threshold`, `critical_behind_threshold`) to `mt_event_progression`
+and the async daemon writes them from runtime state. The shard-state selector
+reads them back into `ShardState` so monitoring tooling such as CritterWatch can
+display per-shard health.
+
+```cs
+opts.Events.EnableExtendedProgressionTracking = true;
+```
+
+Marten 9.7 also implements the storage-agnostic
+[`IEventStoreInstrumentation`](https://github.com/JasperFx/jasperfx/blob/main/src/JasperFx.Events/IEventStoreInstrumentation.cs)
+abstraction from JasperFx.Events 2.9.0 — `Wolverine.CritterWatch.Marten` and
+similar satellite packages flip the same toggle via the interface without
+referencing Marten's concrete `EventGraph`:
+
+```cs
+((IEventStoreInstrumentation)opts.Events).ExtendedProgressionEnabled = true;
+```
+
+Both names refer to the same underlying field. New code is encouraged to prefer
+the interface property; `EnableExtendedProgressionTracking` continues to work
+without deprecation warnings.
+
## Advanced Skipping Tracking
::: info
diff --git a/src/CoreTests/Events/EventGraph_IEventStoreInstrumentation.cs b/src/CoreTests/Events/EventGraph_IEventStoreInstrumentation.cs
new file mode 100644
index 0000000000..24cd1eef5a
--- /dev/null
+++ b/src/CoreTests/Events/EventGraph_IEventStoreInstrumentation.cs
@@ -0,0 +1,75 @@
+using JasperFx.Events;
+using Marten.Events;
+using Shouldly;
+using Xunit;
+
+namespace CoreTests.Events;
+
+// #4686 — EventGraph implements JasperFx.Events.IEventStoreInstrumentation (shipped in
+// JasperFx 2.9.0, jasperfx#424). Both the legacy EnableExtendedProgressionTracking property
+// and the storage-agnostic ExtendedProgressionEnabled name on IEventStoreInstrumentation must
+// alias the same underlying field, so Wolverine.CritterWatch.Marten can flip the toggle via
+// the abstraction without breaking existing code that sets EnableExtendedProgressionTracking
+// directly.
+public class EventGraph_IEventStoreInstrumentation
+{
+ private static EventGraph BuildEventGraph()
+ {
+ // EventGraph requires a StoreOptions to wire up its registry, but a default
+ // StoreOptions is sufficient here -- no connection or schema interaction.
+ return new EventGraph(new Marten.StoreOptions());
+ }
+
+ [Fact]
+ public void implements_event_store_instrumentation()
+ {
+ BuildEventGraph().ShouldBeAssignableTo();
+ }
+
+ [Fact]
+ public void default_is_disabled()
+ {
+ var events = BuildEventGraph();
+
+ events.EnableExtendedProgressionTracking.ShouldBeFalse();
+ events.ExtendedProgressionEnabled.ShouldBeFalse();
+ ((IEventStoreInstrumentation)events).ExtendedProgressionEnabled.ShouldBeFalse();
+ }
+
+ [Fact]
+ public void enabling_via_legacy_name_flows_to_interface_property()
+ {
+ var events = BuildEventGraph();
+ var instrumentation = (IEventStoreInstrumentation)events;
+
+ events.EnableExtendedProgressionTracking = true;
+
+ events.ExtendedProgressionEnabled.ShouldBeTrue();
+ instrumentation.ExtendedProgressionEnabled.ShouldBeTrue();
+ }
+
+ [Fact]
+ public void enabling_via_interface_flows_to_legacy_name()
+ {
+ var events = BuildEventGraph();
+ var instrumentation = (IEventStoreInstrumentation)events;
+
+ instrumentation.ExtendedProgressionEnabled = true;
+
+ events.EnableExtendedProgressionTracking.ShouldBeTrue();
+ events.ExtendedProgressionEnabled.ShouldBeTrue();
+ }
+
+ [Fact]
+ public void disabling_via_either_surface_unwinds_the_other()
+ {
+ var events = BuildEventGraph();
+ var instrumentation = (IEventStoreInstrumentation)events;
+
+ events.EnableExtendedProgressionTracking = true;
+ instrumentation.ExtendedProgressionEnabled = false;
+
+ events.EnableExtendedProgressionTracking.ShouldBeFalse();
+ events.ExtendedProgressionEnabled.ShouldBeFalse();
+ }
+}
diff --git a/src/Marten/Events/EventGraph.cs b/src/Marten/Events/EventGraph.cs
index 850d76c475..134894bed8 100644
--- a/src/Marten/Events/EventGraph.cs
+++ b/src/Marten/Events/EventGraph.cs
@@ -41,7 +41,8 @@ namespace Marten.Events;
Justification = "Class-level: uses Type.MakeGenericType / MethodInfo.MakeGenericMethod / Activator.CreateInstance / FastExpressionCompiler — runtime code generation. AOT consumers pre-generate codegen artifacts (codegen write) and supply source-generator-backed serializer impls per the AOT publishing guide.")]
public partial class EventGraph: EventRegistry, IEventStoreOptions, IReadOnlyEventStoreOptions,
IDisposable, IAsyncDisposable,
- IAggregationSourceFactory, IDescribeMyself
+ IAggregationSourceFactory, IDescribeMyself,
+ IEventStoreInstrumentation
{
private readonly Cache _aggregateNameByType =
new(type => type.IsGenericType ? type.ShortNameInCode() : type.Name.ToTableAlias());
@@ -232,10 +233,30 @@ public override IEvent BuildEvent(object eventData)
public bool EnableEventSkippingInProjectionsOrSubscriptions { get; set; }
///
- /// When enabled, adds heartbeat, agent_status, pause_reason, and running_on_node
- /// columns to the event progression table for CritterWatch monitoring
+ /// When enabled, adds heartbeat, agent_status, pause_reason, running_on_node, and
+ /// warning/critical-behind-threshold columns to the event progression table for
+ /// CritterWatch monitoring.
+ ///
+ /// This is the long-standing Marten-side toggle; #4686 added the storage-agnostic
+ /// as a sibling so
+ /// store-agnostic monitoring tooling (e.g. Wolverine.CritterWatch.Marten) can flip
+ /// the switch via the JasperFx.Events abstraction without referencing Marten's concrete
+ /// option type. Both names refer to the same underlying field; new code is encouraged to
+ /// prefer ExtendedProgressionEnabled on the interface.
+ ///
///
- public bool EnableExtendedProgressionTracking { get; set; }
+ public bool EnableExtendedProgressionTracking
+ {
+ get => ExtendedProgressionEnabled;
+ set => ExtendedProgressionEnabled = value;
+ }
+
+ ///
+ /// -- the primary
+ /// (storage-agnostic) toggle that controls extended progression tracking. Aliased by the
+ /// legacy property.
+ ///
+ public bool ExtendedProgressionEnabled { get; set; }
public bool UseArchivedStreamPartitioning { get; set; }
public bool UseListenNotifyForEventAppends { get; set; }