ProcrastiN8 0.1.1-prerelease0045

This is a prerelease version of ProcrastiN8.
dotnet add package ProcrastiN8 --version 0.1.1-prerelease0045
                    
NuGet\Install-Package ProcrastiN8 -Version 0.1.1-prerelease0045
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ProcrastiN8" Version="0.1.1-prerelease0045" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ProcrastiN8" Version="0.1.1-prerelease0045" />
                    
Directory.Packages.props
<PackageReference Include="ProcrastiN8" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ProcrastiN8 --version 0.1.1-prerelease0045
                    
#r "nuget: ProcrastiN8, 0.1.1-prerelease0045"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ProcrastiN8@0.1.1-prerelease0045
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ProcrastiN8&version=0.1.1-prerelease0045&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=ProcrastiN8&version=0.1.1-prerelease0045&prerelease
                    
Install as a Cake Tool

🐢 ProcrastiN8

ProcrastiN8 is a C#/.NET utility library for simulating productivity, stalling, and quantum-level procrastination. All features are absurd in concept, but real in implementation.

✨ Project Status

SHIT Compliant Build Coverage NuGet


🚀 Getting Started

Install via NuGet:

# .NET CLI
dotnet add package ProcrastiN8 --prerelease
# Because release-readiness is a social construct.

or

# NuGet Package Manager
Install-Package ProcrastiN8 -Prerelease
# Because final releases are for the overly decisive.

Minimal usage:

using ProcrastiN8;

var registry = new QuantumEntanglementRegistry<int>();
var promise = new QuantumPromise<int>(() => Task.FromResult(42), TimeSpan.FromSeconds(2));
registry.Entangle(promise);
var result = await promise.ObserveAsync();
Console.WriteLine($"Collapse result: {result}");

ProcrastinationScheduler

Defer execution ceremonially using configurable procrastination strategies while emitting structured diagnostics worthy of an internal audit.

Diagnostics & Metrics:

  • ActivitySource: ProcrastiN8.Procrastination (spans per scheduling session if you start one)
  • Meter counters (unconditional emission): procrastination.cycles, procrastination.excuses, procrastination.executions, procrastination.triggered, procrastination.abandoned
  • Structured events: ProcrastinationObserverEvent (types: cycle, excuse, triggered, abandoned, executed) automatically recorded by the strategy base
  • Correlation: every run has a CorrelationId (GUID) for trace stitching

Result enrichment fields:

  • StartedUtc, CompletedUtc, TotalDeferral, CyclesPerSecond
  • Flags: Executed, Triggered, Abandoned
  • Counters: Cycles, ExcuseCount
  • ProductivityIndex (satirical: Executed ? 1 / (1 + ExcuseCount + Cycles) : 0)

Basic fire-and-forget:

await ProcrastiN8.Services.ProcrastinationScheduler.Schedule(
    () => DoImportantWorkAsync(),
    initialDelay: TimeSpan.FromSeconds(5),
    mode: ProcrastinationMode.MovingTarget);

Capture metrics:

var result = await ProcrastinationScheduler.ScheduleWithResult(
    () => DoImportantWorkAsync(),
    TimeSpan.FromMilliseconds(250),
    ProcrastinationMode.WeekendFallback);

Console.WriteLine($"Executed={result.Executed} Cycles={result.Cycles} Excuses={result.ExcuseCount} Triggered={result.Triggered} Abandoned={result.Abandoned}");

Interactive control (handle):

var handle = ProcrastinationScheduler.ScheduleWithHandle(
    () => DoImportantWorkAsync(),
    TimeSpan.FromSeconds(30),
    ProcrastinationMode.InfiniteEstimation);

// later externally force progress
handle.TriggerNow();
var summary = await handle.Completion; // includes Triggered=true

Fluent builder (for DI friendliness) with observers:

var scheduler = ProcrastinationSchedulerBuilder
    .Create()
    .WithSafety(new CustomSafety(maxCycles: 200)) // ambient safety override (optional)
    .WithMetrics() // optional: auto-metrics already emit counters; observer adds extensibility
    .AddObserver(new LoggingProcrastinationObserver(new DefaultLogger()))
    .Build();

var r = await scheduler.ScheduleWithResult(
    () => DoImportantWorkAsync(),
    TimeSpan.FromMilliseconds(100),
    ProcrastinationMode.MovingTarget);
Middleware Pipeline

Wrap strategy execution with reusable cross‑cutting procrastination enhancers:

// Custom middleware that annotates before/after execution
sealed class AnnotationMiddleware : IProcrastinationMiddleware
{
    public async Task InvokeAsync(ProcrastinationExecutionContext ctx, Func<Task> next, CancellationToken ct)
    {
        Console.WriteLine($"[MW] entering {ctx.Mode} {ctx.CorrelationId}");
        await next();
        // ctx.Result may be populated after the core strategy completes
        Console.WriteLine($"[MW] leaving; executed={ctx.Result?.Executed}");
    }
}

var scheduler = ProcrastinationSchedulerBuilder.Create()
    .AddMiddleware(new AnnotationMiddleware())
    .AddObserver(new MetricsObserver()) // forwards structured events to counters
    .Build();

var outcome = await scheduler.ScheduleWithResult(
    () => DoImportantWorkAsync(),
    TimeSpan.FromMilliseconds(50),
    ProcrastinationMode.MovingTarget);

Middleware order is preserved: added first = runs outermost (its before executes first, its after executes last). Middleware should be side‑effect minimal and respect the supplied CancellationToken. context.Result is assigned immediately after core strategy completion and is never null post‑execution (it may indicate Executed=false for strategies that exit without running the task such as ambient safety cap or untriggered InfiniteEstimation).

Metrics Observer

MetricsObserver is optional; counters already increment automatically. Attach it if you need to observe events for custom sinks.

Extension Points (Excerpt)
Surface Purpose
IExcuseProvider Generate ceremonial rationalizations.
IDelayStrategy Control temporal pacing (inject fakes in tests).
IProcrastinationStrategyFactory Provide custom / composite / conditional strategies.
IProcrastinationObserver Observe lifecycle transitions and structured events.
IProcrastinationMiddleware Wrap execution with cross‑cutting logic (metrics, logging, chaos).
IProcrastinationSchedulerBuilder Fluent, DI‑friendly construction with observers & middleware.

See full details (sequence diagram, FAQ) in docs/procrastination-scheduler.md.

Safety & Correlation

Ambient safety (WithSafety) sets a process-wide MaxCycles applied to strategies lacking an explicit override. Correlation IDs are assigned before strategy execution so middleware, observers, and results share the same GUID even if execution is abandoned, capped, or triggered early.

Extended Factory Example
sealed class CustomFactory : IProcrastinationStrategyFactory
{
    public IProcrastinationStrategy Create(ProcrastinationMode mode) => mode switch
    {
        ProcrastinationMode.MovingTarget => new CompositeProcrastinationStrategy(
            new MovingTargetStrategy(),
            new WeekendFallbackStrategy()),
        ProcrastinationMode.InfiniteEstimation => new ConditionalProcrastinationStrategy(
            new InfiniteEstimationStrategy(),
            new MovingTargetStrategy(),
            tp => tp.GetUtcNow().DayOfWeek == DayOfWeek.Friday),
        _ => new MovingTargetStrategy()
    };
}

var scheduler = ProcrastinationSchedulerBuilder.Create()
    .WithFactory(new CustomFactory())
    .WithSafety(new CustomSafety(300))
    .Build();

🧑‍💻 Usage & API

QuantumEntanglementRegistry

Manages a set of entangled QuantumPromise<T> instances. Only the registry coordinates the collapse of entangled promises according to the configured quantum behavior. Thread-safe, for those who pretend it matters.

var registry = new QuantumEntanglementRegistry<int>();
var promise1 = new QuantumPromise<int>(() => Task.FromResult(42), TimeSpan.FromSeconds(2));
var promise2 = new QuantumPromise<int>(() => Task.FromResult(99), TimeSpan.FromSeconds(2));
registry.Entangle(promise1);
registry.Entangle(promise2);
var result = await promise1.ObserveAsync(); // Collapses all entangled promises if entangled

Expected output:

[QuantumEntanglement] Entangled set: 2 promises
[QuantumEntanglement] Collapsing one promise...
[QuantumEntanglement] Ripple collapse triggered for remaining entangled promises
Collapse result: 42

QuantumPromise

Represents a value in quantum superposition until observed. If entangled in a registry, calling ObserveAsync() will trigger the registry's collapse behavior for all entangled promises. Forced collapse to a specific value is only possible via the registry and the appropriate collapse behavior (e.g., Copenhagen). Observing a promise outside a registry will not trigger entanglement effects.

Eventually

await Eventually.Do(async () =>
{
    Console.WriteLine("Actually doing something now...");
});

Expected output:

[Eventually] Thinking about it... Might do it in 17.3s.
[Eventually] Reason for delay: Blocked by a third-party library.
[Eventually] Just five more minutes...
...
[Eventually] Finally did the thing. You're welcome.

RetryUntilCancelled

await RetryUntilCancelled.Do(
    async () => { Console.WriteLine("Trying again..."); },
    logger: null,
    excuseProvider: null,
    retryStrategy: null,
    cancellationToken: CancellationToken.None);

Expected output:

[Retry] Attempt 1 failed. Retrying in 2.1s...
[Retry] Excuse: Still waiting for the backlog.
[Retry] Attempt 2 failed. Retrying in 4.2s...
... (until cancelled)

FakeProgress, InfiniteSpinner, BusyWaitSimulator

await FakeProgress.RunAsync("Deploying to production", steps: 5, logger: null, cancellationToken: CancellationToken.None);

Expected output:

[FakeProgress] Aligning expectations...
[FakeProgress] Calibrating metrics...
[FakeProgress] Pretending to load data...
[FakeProgress] Synchronizing with imaginary server...
[FakeProgress] Consulting committee of doubts...
[FakeProgress] Progress: 100% (allegedly)

🫣 UncertaintyDelay

A delay that changes unpredictably each time you check it.

Overview

UncertaintyDelay is a utility for introducing unpredictable delays in asynchronous workflows. It leverages IRandomProvider for testable randomness and supports excuses for each delay round.

Example Usage
using ProcrastiN8.LazyTasks;

await UncertaintyDelay.WaitAsync(
    maxDelay: TimeSpan.FromSeconds(5),
    cancellationToken: CancellationToken.None);
Features
  • Randomized delay durations.
  • Configurable number of delay rounds.
  • Optional excuse logging for each delay.

🤖 OpenAI Excuse Provider

ProcrastiN8 includes an OpenAIExcuseProvider that fetches creative excuses from OpenAI's ChatGPT API. This provider is ideal for generating topical and humorous excuses for procrastination.

Usage

using ProcrastiN8.Common;

var excuseProvider = new OpenAIExcuseProvider("your-api-key");
var excuse = await excuseProvider.GetExcuseAsync();
Console.WriteLine(excuse);

Note: You need an OpenAI API key to use this feature.


📦 NuGet

ProcrastiN8 is available on NuGet


📝 License

MIT License. See LICENSE.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.1-prerelease0045 144 8/24/2025
0.1.1-prerelease0037 142 8/24/2025
0.1.1-prerelease0019 94 7/30/2025
0.1.1-prerelease0018 90 7/29/2025
0.1.1-prerelease0017 91 7/29/2025
0.1.1-prerelease0016 91 7/28/2025
0.1.1-prerelease0015 91 7/28/2025
0.1.1-prerelease0011 95 7/28/2025
0.1.1-prerelease0009 95 7/28/2025
0.1.1-prerelease0008 89 7/28/2025
0.1.1-prerelease0001 93 7/27/2025
0.1.0-prerelease0001 508 7/22/2025

Built by procrastination, driven by satire.