diff --git a/Directory.Build.props b/Directory.Build.props index 0582a71..ea51a65 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,7 +8,7 @@ - false + true diff --git a/Directory.Packages.props b/Directory.Packages.props index df62f0f..a62cdb4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -2,7 +2,7 @@ true - + @@ -29,18 +29,18 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/INDEX.md b/INDEX.md index 48274c5..a373e53 100644 --- a/INDEX.md +++ b/INDEX.md @@ -12,6 +12,7 @@ Project overview: see [README](/README.md). - Solution: [/src/Coven.sln](/src/Coven.sln) - Core: [/src/Coven.Core](/src/Coven.Core/) ([README](/src/Coven.Core/README.md)) +- Core Debug: [/src/Coven.Core.Debug](/src/Coven.Core.Debug/) ([README](/src/Coven.Core.Debug/README.md)) - Streaming: [/src/Coven.Core.Streaming](/src/Coven.Core.Streaming/) ([README](/src/Coven.Core.Streaming/README.md)) - Daemonology: [/src/Coven.Daemonology](/src/Coven.Daemonology/) ([README](/src/Coven.Daemonology/README.md)) - Transmutation: [/src/Coven.Transmutation](/src/Coven.Transmutation/) ([README](/src/Coven.Transmutation/README.md)) diff --git a/src/Coven.Agents.OpenAI/ServiceCollectionExtensions.cs b/src/Coven.Agents.OpenAI/OpenAIAgentsServiceCollectionExtensions.cs similarity index 98% rename from src/Coven.Agents.OpenAI/ServiceCollectionExtensions.cs rename to src/Coven.Agents.OpenAI/OpenAIAgentsServiceCollectionExtensions.cs index 96f706c..cb42167 100644 --- a/src/Coven.Agents.OpenAI/ServiceCollectionExtensions.cs +++ b/src/Coven.Agents.OpenAI/OpenAIAgentsServiceCollectionExtensions.cs @@ -16,7 +16,7 @@ namespace Coven.Agents.OpenAI; /// Dependency Injection helpers for wiring the OpenAI agent integration. /// Registers journals, gateway connection, transmuters, windowing daemons, and the official OpenAI client. /// -public static class ServiceCollectionExtensions +public static class OpenAIAgentsServiceCollectionExtensions { /// /// Registers OpenAI agents with required defaults. @@ -75,7 +75,7 @@ public static IServiceCollection AddOpenAIAgents(this IServiceCollection service }); // Journals and gateway - services.TryAddSingleton, InMemoryScrivener>(); + services.TryAddScoped, InMemoryScrivener>(); services.AddKeyedScoped, InMemoryScrivener>("Coven.InternalOpenAIScrivener"); services.AddScoped, OpenAIScrivener>(); if (registration.StreamingEnabled) diff --git a/src/Coven.Agents.OpenAI/OpenAIScrivener.cs b/src/Coven.Agents.OpenAI/OpenAIScrivener.cs index f05f025..815a4fe 100644 --- a/src/Coven.Agents.OpenAI/OpenAIScrivener.cs +++ b/src/Coven.Agents.OpenAI/OpenAIScrivener.cs @@ -6,47 +6,46 @@ namespace Coven.Agents.OpenAI; -internal sealed class OpenAIScrivener : IScrivener +/// +/// OpenAI scrivener wrapper that forwards outbound efferent entries to the OpenAI gateway +/// and persists all entries to the inner journal; logs the append for observability. +/// +internal sealed class OpenAIScrivener : TappedScrivener { - private readonly IScrivener _inner; private readonly IOpenAIGatewayConnection _gateway; private readonly ILogger _logger; + /// + /// Creates an instance wrapping a keyed inner scrivener and an OpenAI gateway connection. + /// + /// The keyed inner scrivener used for storage. + /// Gateway connection for sending OpenAI requests. + /// Logger for diagnostic breadcrumbs. public OpenAIScrivener( [FromKeyedServices("Coven.InternalOpenAIScrivener")] IScrivener inner, IOpenAIGatewayConnection gateway, ILogger logger) + : base(inner) { - ArgumentNullException.ThrowIfNull(inner); ArgumentNullException.ThrowIfNull(gateway); ArgumentNullException.ThrowIfNull(logger); - _inner = inner; _gateway = gateway; _logger = logger; } - public async Task WriteAsync(OpenAIEntry entry, CancellationToken cancellationToken = default) + /// + /// Sends entries via the gateway and appends all entries to the inner scrivener; + /// logs the append with the assigned position. + /// + public override async Task WriteAsync(OpenAIEntry entry, CancellationToken cancellationToken = default) { if (entry is OpenAIEfferent outgoing) { await _gateway.SendAsync(outgoing, cancellationToken).ConfigureAwait(false); } - long pos = await _inner.WriteAsync(entry, cancellationToken).ConfigureAwait(false); + long pos = await WriteInnerAsync(entry, cancellationToken).ConfigureAwait(false); OpenAILog.OpenAIScrivenerAppended(_logger, entry.GetType().Name, pos); return pos; } - - public IAsyncEnumerable<(long journalPosition, OpenAIEntry entry)> TailAsync(long afterPosition = 0, CancellationToken cancellationToken = default) - => _inner.TailAsync(afterPosition, cancellationToken); - - public IAsyncEnumerable<(long journalPosition, OpenAIEntry entry)> ReadBackwardAsync(long beforePosition = long.MaxValue, CancellationToken cancellationToken = default) - => _inner.ReadBackwardAsync(beforePosition, cancellationToken); - - public Task<(long journalPosition, OpenAIEntry entry)> WaitForAsync(long afterPosition, Func match, CancellationToken cancellationToken = default) - => _inner.WaitForAsync(afterPosition, match, cancellationToken); - - public Task<(long journalPosition, TDerived entry)> WaitForAsync(long afterPosition, Func match, CancellationToken cancellationToken = default) - where TDerived : OpenAIEntry - => _inner.WaitForAsync(afterPosition, match, cancellationToken); } diff --git a/src/Coven.Chat.Console/ServiceCollectionExtensions.cs b/src/Coven.Chat.Console/ConsoleChatServiceCollectionExtensions.cs similarity index 92% rename from src/Coven.Chat.Console/ServiceCollectionExtensions.cs rename to src/Coven.Chat.Console/ConsoleChatServiceCollectionExtensions.cs index d336090..d231fc5 100644 --- a/src/Coven.Chat.Console/ServiceCollectionExtensions.cs +++ b/src/Coven.Chat.Console/ConsoleChatServiceCollectionExtensions.cs @@ -1,10 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 using Coven.Core; -using Microsoft.Extensions.DependencyInjection.Extensions; using Coven.Daemonology; using Coven.Transmutation; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; namespace Coven.Chat.Console; @@ -12,7 +12,7 @@ namespace Coven.Chat.Console; /// Dependency Injection helpers for wiring the Console chat adapter. /// Registers gateway/session components, journals, the Console↔Chat transmuter, and the console daemon. /// -public static class ServiceCollectionExtensions +public static class ConsoleChatServiceCollectionExtensions { /// /// Adds Console chat integration using the provided client configuration. @@ -29,7 +29,7 @@ public static IServiceCollection AddConsoleChat(this IServiceCollection services services.AddScoped(); // Default ChatEntry journal if none provided by host - services.TryAddSingleton, InMemoryScrivener>(); + services.TryAddScoped, InMemoryScrivener>(); services.AddScoped, ConsoleScrivener>(); services.AddKeyedScoped, InMemoryScrivener>("Coven.InternalConsoleScrivener"); diff --git a/src/Coven.Chat.Console/ConsoleScrivener.cs b/src/Coven.Chat.Console/ConsoleScrivener.cs index f08ef27..6ed0d98 100644 --- a/src/Coven.Chat.Console/ConsoleScrivener.cs +++ b/src/Coven.Chat.Console/ConsoleScrivener.cs @@ -6,9 +6,12 @@ namespace Coven.Chat.Console; -internal sealed class ConsoleScrivener : IScrivener +/// +/// Console chat scrivener wrapper that forwards outbound efferent entries to the console gateway +/// and persists all entries to the inner journal for deterministic ordering and observation. +/// +internal sealed class ConsoleScrivener : TappedScrivener { - private readonly IScrivener _scrivener; private readonly ConsoleGatewayConnection _gateway; private readonly ILogger _logger; @@ -16,38 +19,27 @@ public ConsoleScrivener( [FromKeyedServices("Coven.InternalConsoleScrivener")] IScrivener scrivener, ConsoleGatewayConnection gateway, ILogger logger) + : base(scrivener) { - ArgumentNullException.ThrowIfNull(scrivener); ArgumentNullException.ThrowIfNull(gateway); ArgumentNullException.ThrowIfNull(logger); - _scrivener = scrivener; _gateway = gateway; _logger = logger; } - public async Task WriteAsync(ConsoleEntry entry, CancellationToken cancellationToken = default) + /// + /// Sends entries to the console gateway and appends + /// all entries to the inner scrivener; logs the append with the assigned position. + /// + public override async Task WriteAsync(ConsoleEntry entry, CancellationToken cancellationToken = default) { if (entry is ConsoleEfferent) { await _gateway.SendAsync(entry.Text, cancellationToken).ConfigureAwait(false); } - long pos = await _scrivener.WriteAsync(entry, cancellationToken).ConfigureAwait(false); + long pos = await WriteInnerAsync(entry, cancellationToken).ConfigureAwait(false); ConsoleLog.ConsoleScrivenerAppended(_logger, entry.GetType().Name, pos); return pos; } - - public IAsyncEnumerable<(long journalPosition, ConsoleEntry entry)> TailAsync(long afterPosition = 0, CancellationToken cancellationToken = default) - => _scrivener.TailAsync(afterPosition, cancellationToken); - - public IAsyncEnumerable<(long journalPosition, ConsoleEntry entry)> ReadBackwardAsync(long beforePosition = long.MaxValue, CancellationToken cancellationToken = default) - => _scrivener.ReadBackwardAsync(beforePosition, cancellationToken); - - public Task<(long journalPosition, ConsoleEntry entry)> WaitForAsync(long afterPosition, Func match, CancellationToken cancellationToken = default) - => _scrivener.WaitForAsync(afterPosition, match, cancellationToken); - - public Task<(long journalPosition, TDerived entry)> WaitForAsync(long afterPosition, Func match, CancellationToken cancellationToken = default) - where TDerived : ConsoleEntry - => _scrivener.WaitForAsync(afterPosition, match, cancellationToken); } - diff --git a/src/Coven.Chat.Discord/ServiceCollectionExtensions.cs b/src/Coven.Chat.Discord/DiscordChatServiceCollectionExtensions.cs similarity index 95% rename from src/Coven.Chat.Discord/ServiceCollectionExtensions.cs rename to src/Coven.Chat.Discord/DiscordChatServiceCollectionExtensions.cs index eb8982d..a705208 100644 --- a/src/Coven.Chat.Discord/ServiceCollectionExtensions.cs +++ b/src/Coven.Chat.Discord/DiscordChatServiceCollectionExtensions.cs @@ -17,7 +17,7 @@ namespace Coven.Chat.Discord; /// Dependency Injection helpers for wiring the Discord chat adapter. /// Registers the Discord client, session factory, journals, transmuter, daemon, and default windowing policies. /// -public static class ServiceCollectionExtensions +public static class DiscordChatServiceCollectionExtensions { /// /// Adds Discord chat integration using the provided client configuration. @@ -45,7 +45,7 @@ public static IServiceCollection AddDiscordChat(this IServiceCollection services services.AddScoped(); // Default ChatEntry journal if none provided by host - services.TryAddSingleton, InMemoryScrivener>(); + services.TryAddScoped, InMemoryScrivener>(); services.AddScoped, DiscordScrivener>(); services.AddKeyedScoped, InMemoryScrivener>("Coven.InternalDiscordScrivener"); diff --git a/src/Coven.Chat.Discord/DiscordScrivener.cs b/src/Coven.Chat.Discord/DiscordScrivener.cs index b6d24b2..ab8aec3 100644 --- a/src/Coven.Chat.Discord/DiscordScrivener.cs +++ b/src/Coven.Chat.Discord/DiscordScrivener.cs @@ -6,30 +6,36 @@ namespace Coven.Chat.Discord; -internal sealed class DiscordScrivener : IScrivener +/// +/// Discord chat scrivener wrapper that forwards outbound efferent entries to Discord via the gateway +/// and persists all entries to the inner journal so pumps/tests can observe ordering. +/// +internal sealed class DiscordScrivener : TappedScrivener { - private readonly IScrivener _scrivener; private readonly DiscordGatewayConnection _discordClient; private readonly ILogger _logger; /// /// Wraps a keyed inner scrivener and forwards outbound efferent messages to Discord. /// - /// The keyed inner used for storage. + /// The keyed inner scrivener used for storage. /// The gateway connection for sending messages to Discord. /// Logger for diagnostic breadcrumbs. - /// Because we are what we utilize, ensure that the inner scrivener is keyed in DI. + /// Ensure the inner scrivener is keyed in DI. public DiscordScrivener([FromKeyedServices("Coven.InternalDiscordScrivener")] IScrivener scrivener, DiscordGatewayConnection discordClient, ILogger logger) + : base(scrivener) { - ArgumentNullException.ThrowIfNull(scrivener); ArgumentNullException.ThrowIfNull(discordClient); ArgumentNullException.ThrowIfNull(logger); - _scrivener = scrivener; _discordClient = discordClient; _logger = logger; } - public async Task WriteAsync(DiscordEntry entry, CancellationToken cancellationToken = default) + /// + /// Sends entries to Discord via the gateway and appends + /// all entries to the inner scrivener; logs the append with the assigned position. + /// + public override async Task WriteAsync(DiscordEntry entry, CancellationToken cancellationToken = default) { // Only send actual outbound messages to Discord. ACKs and inbound entries must not be sent. if (entry is DiscordEfferent) @@ -38,21 +44,8 @@ public async Task WriteAsync(DiscordEntry entry, CancellationToken cancell } // Always persist to the underlying scrivener so pumps and tests can observe ordering. - long pos = await _scrivener.WriteAsync(entry, cancellationToken).ConfigureAwait(false); + long pos = await WriteInnerAsync(entry, cancellationToken).ConfigureAwait(false); DiscordLog.DiscordScrivenerAppended(_logger, entry.GetType().Name, pos); return pos; } - - public IAsyncEnumerable<(long journalPosition, DiscordEntry entry)> TailAsync(long afterPosition = 0, CancellationToken cancellationToken = default) - => _scrivener.TailAsync(afterPosition, cancellationToken); - - public IAsyncEnumerable<(long journalPosition, DiscordEntry entry)> ReadBackwardAsync(long beforePosition = long.MaxValue, CancellationToken cancellationToken = default) - => _scrivener.ReadBackwardAsync(beforePosition, cancellationToken); - - public Task<(long journalPosition, DiscordEntry entry)> WaitForAsync(long afterPosition, Func match, CancellationToken cancellationToken = default) - => _scrivener.WaitForAsync(afterPosition, match, cancellationToken); - - public Task<(long journalPosition, TDerived entry)> WaitForAsync(long afterPosition, Func match, CancellationToken cancellationToken = default) - where TDerived : DiscordEntry - => _scrivener.WaitForAsync(afterPosition, match, cancellationToken); } diff --git a/src/Coven.Chat/ServiceCollectionExtensions.cs b/src/Coven.Chat/ChatWindowingServiceCollectionExtensions.cs similarity index 94% rename from src/Coven.Chat/ServiceCollectionExtensions.cs rename to src/Coven.Chat/ChatWindowingServiceCollectionExtensions.cs index f76e83d..52f7704 100644 --- a/src/Coven.Chat/ServiceCollectionExtensions.cs +++ b/src/Coven.Chat/ChatWindowingServiceCollectionExtensions.cs @@ -13,7 +13,7 @@ namespace Coven.Chat; /// Adds generic chat windowing infrastructure (journal, daemon) with a DI-provided window policy. /// Useful for chunking and emitting grouped as entries. /// -public static class ServiceCollectionExtensions +public static class ChatWindowingServiceCollectionExtensions { /// /// Registers chat windowing components and a windowing daemon for . @@ -26,7 +26,7 @@ public static IServiceCollection AddChatWindowing(this IServiceCollection servic ArgumentNullException.ThrowIfNull(services); // Ensure required journals exist - services.TryAddSingleton, InMemoryScrivener>(); + services.TryAddScoped, InMemoryScrivener>(); services.TryAddSingleton, InMemoryScrivener>(); // Register generic windowing daemon for Chat using a DI-provided policy diff --git a/src/Coven.Core.Debug/Coven.Core.Debug.csproj b/src/Coven.Core.Debug/Coven.Core.Debug.csproj new file mode 100644 index 0000000..2fcd78b --- /dev/null +++ b/src/Coven.Core.Debug/Coven.Core.Debug.csproj @@ -0,0 +1,19 @@ + + + net10.0 + true + true + $(MSBuildProjectName) + Diagnostics helpers for Coven.Core (e.g., scrivener dump finalizer). + Coven + coven;debug;diagnostics;journal;dump + + + + + + + + + + diff --git a/src/Coven.Core.Debug/README.md b/src/Coven.Core.Debug/README.md new file mode 100644 index 0000000..5f923dc --- /dev/null +++ b/src/Coven.Core.Debug/README.md @@ -0,0 +1,25 @@ +# Coven.Core.Debug — Scrivener Taps + +Minimal, zero‑ceremony way to observe journal writes without changing any branch/leaf code: wrap an existing `IScrivener` with a tiny decorator that calls an observer delegate after each write. + +## Goal +- Add an observer delegate to an existing scrivener. +- Do not change `IScrivener`. +- Do not write back to the observed journal. +- Preserve normal tail/read behavior and positions. + +## Concept +- `TappedScrivener` has an inner `IScrivener`. +- On `WriteAsync(entry)`, it awaits the inner write to get the assigned `position`, then invokes an observer delegate: `Action`. +- All read APIs (`TailAsync`, `ReadBackwardAsync`, `WaitForAsync`) simply delegate to the inner scrivener. + +## Behavior +- Observation happens after the inner write completes, using the actual assigned position. +- Observer exceptions are ignored (best‑effort, non‑interfering). +- No writes back to the observed journal; tap is read‑only aside from the delegated call. +- Overhead is a single delegate invocation per write. + +## Notes +- Keep observers cheap; they run inline after writes. If you need heavy work, queue it yourself inside the delegate. +- Because it wraps a specific IScrivener instance, it only observes entries written through that instance. +- Because we need to register the TappedScrivener as the final implementation for IScrivener we will need some way to disambiguate them. diff --git a/src/Coven.Core/Builder/ServiceCollectionExtensions.cs b/src/Coven.Core/Builder/CovenServiceBuilder.cs similarity index 76% rename from src/Coven.Core/Builder/ServiceCollectionExtensions.cs rename to src/Coven.Core/Builder/CovenServiceBuilder.cs index 4cb4dd7..bd2d163 100644 --- a/src/Coven.Core/Builder/ServiceCollectionExtensions.cs +++ b/src/Coven.Core/Builder/CovenServiceBuilder.cs @@ -4,47 +4,9 @@ using Coven.Core.Tags; using Coven.Core.Activation; using Coven.Core.Routing; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace Coven.Core.Builder; -/// -/// DI entry points for composing and finalizing a Coven runtime. -/// -public static class ServiceCollectionExtensions -{ - /// - /// Composes a Coven using the provided builder action and ensures finalization. - /// - /// The service collection. - /// Callback to register MagikBlocks and options. - /// The same service collection to enable fluent chaining. - public static IServiceCollection BuildCoven(this IServiceCollection services, Action build) - { - ArgumentNullException.ThrowIfNull(services); - ArgumentNullException.ThrowIfNull(build); - - CovenServiceBuilder builder = new(services); - build(builder); - // Idempotent finalize if user forgot to call Done() - builder.Done(); - return services; - } - - /// - /// Registers a singleton in-memory scrivener for the given entry type. - /// Uses TryAdd to avoid duplicate registrations. - /// - public static IServiceCollection AddInMemoryScrivener(this IServiceCollection services) - where TEntry : notnull - { - ArgumentNullException.ThrowIfNull(services); - - services.TryAddSingleton, InMemoryScrivener>(); - return services; - } -} - /// /// Fluent builder used to register MagikBlocks and finalize the Coven runtime. /// @@ -160,3 +122,4 @@ private void EnsureNotFinalized() } } + diff --git a/src/Coven.Core/Builder/CovenServiceCollectionExtensions.cs b/src/Coven.Core/Builder/CovenServiceCollectionExtensions.cs new file mode 100644 index 0000000..c699f98 --- /dev/null +++ b/src/Coven.Core/Builder/CovenServiceCollectionExtensions.cs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BUSL-1.1 + +using Microsoft.Extensions.DependencyInjection; + +namespace Coven.Core.Builder; + +/// +/// DI entry points for composing and finalizing a Coven runtime. +/// +public static class CovenServiceCollectionExtensions +{ + /// + /// Composes a Coven using the provided builder action and ensures finalization. + /// + /// The service collection. + /// Callback to register MagikBlocks and options. + /// The same service collection to enable fluent chaining. + public static IServiceCollection BuildCoven(this IServiceCollection services, Action build) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(build); + + CovenServiceBuilder builder = new(services); + build(builder); + // Idempotent finalize if user forgot to call Done() + builder.Done(); + return services; + } +} + diff --git a/src/Coven.Core/LambdaTappedScrivener.cs b/src/Coven.Core/LambdaTappedScrivener.cs new file mode 100644 index 0000000..d2ad265 --- /dev/null +++ b/src/Coven.Core/LambdaTappedScrivener.cs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BUSL-1.1 + +namespace Coven.Core; + +/// +/// A tapped scrivener that uses a delegate to implement write behavior. +/// +/// The journal entry type. +/// +/// Initializes a new instance of the . +/// +/// The inner scrivener used for storage and reads. +/// Optional delegate that performs the write; receives the entry, the inner scrivener, and a token. Defaults to pass‑through to the inner. +public sealed class LambdaTappedScrivener( + IScrivener inner, + Func, CancellationToken, Task>? write = null) : TappedScrivener(inner) where TEntry : notnull +{ + private readonly Func, CancellationToken, Task> _write = write ?? ((entry, innerScrivener, ct) => innerScrivener.WriteAsync(entry, ct)); + + + /// + public override Task WriteAsync(TEntry entry, CancellationToken cancellationToken = default) + => _write(entry, Inner, cancellationToken); +} diff --git a/src/Coven.Core/TappedScrivener.cs b/src/Coven.Core/TappedScrivener.cs new file mode 100644 index 0000000..0830551 --- /dev/null +++ b/src/Coven.Core/TappedScrivener.cs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: BUSL-1.1 + +namespace Coven.Core; + +/// +/// Base scrivener wrapper that exposes the underlying scrivener while delegating +/// read/tail/wait operations. Implementers override +/// to perform side‑effects or routing, and can call +/// to append to the inner journal while preserving ordering semantics. +/// +/// The entry type for the journal. +public abstract class TappedScrivener : IScrivener where TEntry : notnull +{ + /// + /// Initializes a new instance of the class. + /// + /// The inner scrivener used for storage and read operations. + protected TappedScrivener(IScrivener inner) + { + ArgumentNullException.ThrowIfNull(inner); + Inner = inner; + } + + /// + /// Implement write behavior for the tapped scrivener. Implementations may call + /// to append to the underlying journal + /// before or after performing side‑effects. + /// + /// The entry to append. + /// A cancellation token. + /// The assigned journal position. + public abstract Task WriteAsync(TEntry entry, CancellationToken cancellationToken = default); + + /// + public IAsyncEnumerable<(long journalPosition, TEntry entry)> TailAsync(long afterPosition = 0, CancellationToken cancellationToken = default) + => Inner.TailAsync(afterPosition, cancellationToken); + + /// + public IAsyncEnumerable<(long journalPosition, TEntry entry)> ReadBackwardAsync(long beforePosition = long.MaxValue, CancellationToken cancellationToken = default) + => Inner.ReadBackwardAsync(beforePosition, cancellationToken); + + /// + public Task<(long journalPosition, TEntry entry)> WaitForAsync(long afterPosition, Func match, CancellationToken cancellationToken = default) + => Inner.WaitForAsync(afterPosition, match, cancellationToken); + + /// + public Task<(long journalPosition, TDerived entry)> WaitForAsync(long afterPosition, Func match, CancellationToken cancellationToken = default) + where TDerived : TEntry + => Inner.WaitForAsync(afterPosition, match, cancellationToken); + + /// Access the inner scrivener directly for advanced scenarios. + protected IScrivener Inner { get; } + + /// + /// Append to the inner scrivener while preserving ordering and position semantics. + /// + protected Task WriteInnerAsync(TEntry entry, CancellationToken cancellationToken = default) + => Inner.WriteAsync(entry, cancellationToken); +} diff --git a/src/Coven.sln b/src/Coven.sln index ca3e10e..1f0e511 100644 --- a/src/Coven.sln +++ b/src/Coven.sln @@ -43,6 +43,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coven.Core.Streaming", "Cov EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coven.Toys.DiscordStreaming", "toys\Coven.Toys.DiscordStreaming\Coven.Toys.DiscordStreaming.csproj", "{08F6A76C-CF62-4113-8740-CF5EF8115F99}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coven.Core.Debug", "Coven.Core.Debug\Coven.Core.Debug.csproj", "{1264DB86-E00B-4222-B865-CF15BD752704}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -269,6 +271,18 @@ Global {08F6A76C-CF62-4113-8740-CF5EF8115F99}.Release|x64.Build.0 = Release|Any CPU {08F6A76C-CF62-4113-8740-CF5EF8115F99}.Release|x86.ActiveCfg = Release|Any CPU {08F6A76C-CF62-4113-8740-CF5EF8115F99}.Release|x86.Build.0 = Release|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Debug|x64.ActiveCfg = Debug|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Debug|x64.Build.0 = Debug|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Debug|x86.ActiveCfg = Debug|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Debug|x86.Build.0 = Debug|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Release|Any CPU.Build.0 = Release|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Release|x64.ActiveCfg = Release|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Release|x64.Build.0 = Release|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Release|x86.ActiveCfg = Release|Any CPU + {1264DB86-E00B-4222-B865-CF15BD752704}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/samples/01.DiscordAgent/DiscordAgent.csproj b/src/samples/01.DiscordAgent/DiscordAgent.csproj index 21d27d8..5f2e204 100644 --- a/src/samples/01.DiscordAgent/DiscordAgent.csproj +++ b/src/samples/01.DiscordAgent/DiscordAgent.csproj @@ -17,12 +17,13 @@ + + -