Optima.Net.NegotiatR 2.0.0

Prefix Reserved
dotnet add package Optima.Net.NegotiatR --version 2.0.0
                    
NuGet\Install-Package Optima.Net.NegotiatR -Version 2.0.0
                    
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="Optima.Net.NegotiatR" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Optima.Net.NegotiatR" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Optima.Net.NegotiatR" />
                    
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 Optima.Net.NegotiatR --version 2.0.0
                    
#r "nuget: Optima.Net.NegotiatR, 2.0.0"
                    
#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 Optima.Net.NegotiatR@2.0.0
                    
#: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=Optima.Net.NegotiatR&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Optima.Net.NegotiatR&version=2.0.0
                    
Install as a Cake Tool

Optima.Net.NegotiatR

Optima.Net.NegotiatR is a deterministic, single-pass negotiation engine.
It answers exactly one question:

�Given these policy failures, may we propose an alternative intent?�

Nothing more. Nothing less.

TL;DR (for people who hate READMEs)

If you don�t want to read this document and would rather see NegotiatR actually doing something,
clone and run the test harness instead:

https://github.com/snamretsuek/Optima.Net.TestHarnesses

It�s contains a console apps that:

  • wire everything together properly,

  • show real negotiation flows (password ? biometrics, etc.),

  • and let you step through the code in a debugger like a normal human.

If you still have questions after running the harness� then yes, you�ll have to come back and read the README.


Companion Package (Important)

NegotiatR is designed to be used together with:

?? Optima.Net.Domain

That package provides:

  • IPolicy<T> and ISpecification<T>
  • IPolicyJustification<T>
  • PolicyDiagnosticEvaluator
  • PolicyFailure and PolicyFailureSemantics
Build Proposal
?
Evaluate policies using PolicyDiagnosticEvaluator (Optima.Net.Domain)
?
Collect PolicyFailure results
?
Pass Proposal + Failures to NegotiatR
?
Apply NegotiatROutcome

NegotiatR does not evaluate policies itself.
It only consumes the failures produced elsewhere.


Table of Contents


What Problem NegotiatR Solves

In real systems, policies fail.

When they do, teams often:

  • throw exceptions
  • retry automatically
  • hardcode fallbacks
  • silently change behavior

This creates implicit decision flows that are:

  • hard to reason about
  • impossible to audit
  • dangerous in regulated systems

NegotiatR forces you to answer a single explicit question:

If this intent failed, are we allowed to suggest another intent?


How NegotiatR Fits With Optima.Net.Domain

Optima.Net.Domain answers:

�Is this proposal acceptable?�

It does this by:

  • evaluating policies
  • executing specifications
  • producing structured diagnostics

NegotiatR answers a different question:

�If it is not acceptable, may we propose something else?�

These two responsibilities must never be mixed.


Core Concepts

Proposal

A proposal represents intent, not execution.

public interface IProposal
{
    ProposalId Id { get; }
}

Examples:

  • Authenticate using Biometrics
  • Authenticate using Password
  • Create a Password
  • Grant Credit
  • Offer Secured Credit

A proposal does not prove success.
It only states what is being attempted.


Policy Failure

NegotiatR consumes policy failures produced by
PolicyDiagnosticEvaluator (Optima.Net.Domain).

public sealed record PolicyFailure(
    PolicyType policyType,
    Code code,
    PolicyFailureSemantics Semantics,
    IReadOnlyDictionary<string, object> Data);

NegotiatR trusts these failures to be correct.


Negotiation Rules

Rules decide whether an alternative intent may be proposed.

They:

  • inspect the proposal
  • inspect policy failures
  • may propose alternatives

They never:

  • evaluate policies
  • validate correctness
  • mutate proposals
  • orchestrate workflows

Negotiation Flow

  1. No failures
    ? NegotiatRAccepted

  2. Failures exist
    ? rules evaluated deterministically

  3. First valid alternative proposed
    ? NegotiatRCounterProposed

  4. No alternatives
    ? NegotiatRRejected

There is:

  • no recursion
  • no re-entry
  • no retry
  • no state mutation

Policy Failure Semantics

Semantics Meaning
Terminal No alternative is allowed
Correctable User may fix and retry
Replaceable Alternative intent may be proposed

NegotiatR enforces nothing.
Rules decide how to react to semantics.


Authentication Example

AuthenticationProposal

public sealed class AuthenticationProposal : IProposal
{
    public ProposalId Id { get; }
    public CredentialType Type { get; }

    private AuthenticationProposal(
        ProposalId id,
        CredentialType type)
    {
        Id = id;
        Type = type;
    }

    public static AuthenticationProposal Password()
        => new AuthenticationProposal(ProposalId.New(), CredentialType.Password);

    public static AuthenticationProposal Biometrics()
        => new AuthenticationProposal(ProposalId.New(), CredentialType.Biometrics);
}

Why this exists

Authentication is about intent:

�Which authentication method should we try?�

Execution, validation, and matching are handled elsewhere.


Password Creation Example

Password creation is not authentication.

It has its own proposal, policies, and specifications.

public sealed class PasswordCreationProposal : IProposal
{
    public ProposalId Id { get; }
    public PasswordCreationContext Context { get; }

    public PasswordCreationProposal(
        ProposalId id,
        PasswordCreationContext context)
    {
        Id = id;
        Context = context;
    }
}

Why this exists

  • Password quality rules do not apply to authentication
  • Authentication availability rules do not apply to creation
  • Mixing the two creates subtle bugs

End-to-End Authentication Negotiation

Scenario

  • User attempts biometric authentication
  • User is not enrolled
  • System may offer password authentication instead

Inputs

var proposal = AuthenticationProposal.Biometrics();

// Failures are produced by PolicyDiagnosticEvaluator (Optima.Net.Domain)
// The following is illustrative only � in real systems you consume diagnostics
        new Dictionary<string, object?>())
};

Rule

public sealed class BiometricsToPasswordRule : INegotiatRRule
{
    public int Priority => 0;
    public Optional<RuleExecutionPhase> Group => Optional<RuleExecutionPhase>.None();

    public bool CanApply(
        IProposal proposal,
        IReadOnlyCollection<PolicyFailure> failures)
        => proposal is AuthenticationProposal a
           && a.Type == CredentialType.Biometrics
           && failures.Any(f => f.Semantics.HasFlag(PolicyFailureSemantics.Replaceable));

    public IEnumerable<IProposal> ProposeAlternatives(
        IProposal proposal,
        IReadOnlyCollection<PolicyFailure> failures)
    {
        yield return AuthenticationProposal.Password();
    }
}

Why this matters

  • No hidden fallbacks
  • No workflow logic
  • Explicit, auditable intent change

FinTech Credit Decision Example

Scenario

  • Customer applies for unsecured credit
  • Risk policy fails
  • System may offer secured credit instead

Proposal

public sealed class CreditApplicationProposal : IProposal
{
    public ProposalId Id { get; }
    public CreditType Type { get; }

    private CreditApplicationProposal(ProposalId id, CreditType type)
    {
        Id = id;
        Type = type;
    }

    public static CreditApplicationProposal Unsecured()
        => new CreditApplicationProposal(ProposalId.New(), CreditType.Unsecured);

    public static CreditApplicationProposal Secured()
        => new CreditApplicationProposal(ProposalId.New(), CreditType.Secured);
}

Rule

public sealed class UnsecuredToSecuredCreditRule : INegotiatRRule
{
    public int Priority => 0;
    public Optional<RuleExecutionPhase> Group => Optional<RuleExecutionPhase>.None();

    public bool CanApply(
        IProposal proposal,
        IReadOnlyCollection<PolicyFailure> failures)
        => proposal is CreditApplicationProposal p
           && p.Type == CreditType.Unsecured
           && failures.Any(f => f.Semantics == PolicyFailureSemantics.Replaceable);

    public IEnumerable<IProposal> ProposeAlternatives(
        IProposal proposal,
        IReadOnlyCollection<PolicyFailure> failures)
    {
        yield return CreditApplicationProposal.Secured();
    }
}

Real-world usage

Banks do this every day.
NegotiatR makes the decision explicit instead of implicit.


Design Rules You Must Not Break

  • NegotiatR never evaluates policies
  • NegotiatR never mutates proposals
  • NegotiatR never retries or loops
  • Rules never perform execution
  • Specifications evaluate proposals only
  • Execution happens outside NegotiatR

If you break these rules, stop and rethink.



About PolicyFailureFactory (Read This)

You will notice references to PolicyFailureFactory in some test code and examples.

Important:
You do not need to use PolicyFailureFactory in real application code.

Why it exists

PolicyFailureFactory is a testing and convenience utility.
It exists to:

  • construct failures in unit tests
  • simplify examples
  • avoid verbose diagnostic setup when illustrating concepts

What you should do in real systems

In production code, failures should come from:

PolicyDiagnosticResult<IProposal> result =
    evaluator.EvaluateAll(policies, proposal);

// Extract immutable policy failures
IReadOnlyCollection<PolicyFailure> failures = result.GetFailures();

Those failures are then passed directly into NegotiatR.

NegotiatR expects failures to be authoritative and does not care how they were produced.

If you find yourself using PolicyFailureFactory in application logic, you are doing it wrong.



Contexts, Attempts, Policies, Specifications (Read This Carefully)

This section fills the most important conceptual gaps.
If you misunderstand these concepts, you will build the wrong system.


What Is a Context?

A context is structured, immutable data attached to a proposal.

A context:

  • contains facts
  • does not contain behavior
  • does not make decisions
  • exists to give specifications something to evaluate

Contexts are not proposals.
Contexts are data carried by proposals.

Example: PasswordAuthenticationContext
public sealed record PasswordAuthenticationContext
{
    public bool IsEnabled { get; }
    public bool HasPassword { get; }
    public bool IsLocked { get; }
    public bool IsTemporarilyBlocked { get; }

    public PasswordAuthenticationContext(
        bool isEnabled,
        bool hasPassword,
        bool isLocked,
        bool isTemporarilyBlocked)
    {
        IsEnabled = isEnabled;
        HasPassword = hasPassword;
        IsLocked = isLocked;
        IsTemporarilyBlocked = isTemporarilyBlocked;
    }
}

This context answers:

  • May password authentication be attempted?

It does not answer:

  • Did authentication succeed?
  • Is the password correct?

Those belong elsewhere.


What Is an Attempt?

An attempt represents ephemeral, per-try data.

Attempts:

  • may be missing (Optional<T>)
  • represent what was tried
  • do not represent system state
Example: BiometricAuthenticationAttempt
public sealed record BiometricAuthenticationAttempt
{
    public BiometricModality Modality { get; }
    public DateTime AttemptedAt { get; }

    public BiometricAuthenticationAttempt(
        BiometricModality modality,
        DateTime attemptedAt)
    {
        Modality = modality;
        AttemptedAt = attemptedAt;
    }
}

Attempts are consumed by:

  • specifications
  • execution logic

Attempts are never stored as long-term truth.


What Is a Specification?

A specification is a pure boolean predicate.

It answers:

Is this proposal acceptable with respect to one rule?

Specifications:

  • evaluate proposals
  • read contexts
  • are deterministic
  • never mutate anything
Example: PasswordExistsSpec
public sealed class PasswordExistsSpec
    : ISpecification<IProposal>
{
    public bool IsSatisfiedBy(IProposal proposal)
    {
        if (proposal is not AuthenticationProposal auth)
            throw new InvalidOperationException();

        if (auth.Type != CredentialType.Password)
            return true;

        return auth.PasswordAuthenticationContext
            .ValueOrThrow()
            .HasPassword;
    }
}

What Is a Policy?

A policy defines when specifications apply and how failures are classified.

Policies:

  • do not evaluate specifications
  • do not contain rules
  • exist to declare scope and semantics
public sealed class PasswordAuthenticationPolicy
    : IPolicy<IProposal>
{
    public bool IsSatisfiedBy(IProposal proposal)
        => proposal is AuthenticationProposal a
           && a.Type == CredentialType.Password;

    public PolicyFailureSemantics FailureSemantics
        => PolicyFailureSemantics.Correctable;
}

What Is a PolicyJustification?

A PolicyJustification is the marriage between:

  • a policy
  • the specifications that justify it

This is intentional and explicit.

public sealed class PasswordAuthenticationPolicyJustification
    : IPolicyJustification<IProposal>
{
    public string PolicyName => nameof(PasswordAuthenticationPolicy);

    public IReadOnlyCollection<ISpecification<IProposal>> Specifications { get; }

    public PasswordAuthenticationPolicyJustification(
        PasswordExistsSpec exists,
        PasswordNotLockedSpec notLocked,
        PasswordNotThrottledSpec notThrottled)
    {
        Specifications = new ISpecification<IProposal>[]
        {
            exists,
            notLocked,
            notThrottled
        };
    }
}

Why this exists:

  • policies alone are meaningless
  • specs alone have no semantics
  • justification binds intent to evidence

How Everything Fits Together

Proposal
  ??? Contexts (facts)
        ??? Attempts (optional, ephemeral)

Policy
  ??? PolicyJustification
        ??? Specifications

PolicyDiagnosticEvaluator
  ??? Produces PolicyFailure[]

NegotiatR
  ??? Consumes failures
  ??? Proposes alternative intent

If you violate this structure, stop.


Summary

  • Optima.Net.Domain decides acceptability
  • Optima.Net.NegotiatR decides alternatives
  • Intent is explicit
  • Decisions are auditable
  • Behavior is deterministic

This separation is the entire point.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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.

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
2.0.0 96 6/23/2026
1.0.3 134 1/8/2026
1.0.2 122 1/7/2026
1.0.1 117 1/1/2026
1.0.0 120 12/31/2025

RELEASENOTES.md