Kumbatio
Core

Inbound demand

demandAdmissionStrategy and resolveDemandOutcome - the recipient's capacity applied to the queue that ignores it

Inbound demand is anything arriving from outside that asks for the user's attention or action: an email, a document comment, a review request, a task assignment, a collaboration invite.

Every triage system in general use is organised around properties of the message - who sent it, how urgent it claims to be, what category it fits. None is organised around the state of the recipient, which is the thing that actually decides whether an arrival is a small task or a crushing weight. This is that variable, applied to the queue.

What this module is, and is not

It is policy, and it is pure. demandAdmissionStrategy resolves the rules for a level; resolveDemandOutcome applies them to one arrival and returns a decision.

It performs no effects. Acknowledging an originator means sending mail, posting a comment, or updating a status chip depending on the app, and those are irreversible in ways an in-process runtime cannot make transactional - unlike the notification gate, whose defer-never-drop guarantee is enforceable precisely because nothing it touches leaves the process. The orchestration, with its ordering, retries, and deduplication, belongs to the consuming app.

The practical shape of that ordering: capture first, acknowledge second. A capture is the reversible half, so a failed acknowledgment can roll it back. An acknowledgment cannot be unsent.

resolveDemandOutcome

function resolveDemandOutcome(
  config: DemandAdmissionConfig,
  autonomy: AutonomyConfig,
  demand: DemandInput,
): DemandOutcome

The pure gating decision - the counterpart of resolveNotificationOutcome, and testable the same way, with no wiring.

Both configs are required because the two questions are genuinely separate: config says what the level's policy wants done, autonomy says how much of it may happen without the user watching.

import {
  autonomyStrategy,
  deferralStrategy,
  demandAdmissionStrategy,
  resolveDemandOutcome,
} from '@kumbatio/energy-system'

const outcome = resolveDemandOutcome(
  engine.resolve(demandAdmissionStrategy),
  engine.resolve(autonomyStrategy),
  { originatorTier: 'unknown', bearsObligation: true, confidence: 0.9 },
)

switch (outcome.admission) {
  case 'live':
    return inbox.deliver(item)
  case 'acknowledge':
    // One act, never two. Capture first - it is the half you can take back.
    await tasks.capture(item, engine.resolve(deferralStrategy).defaultPresetId)
    return replies.acknowledge(item, outcome.acknowledgment)
  case 'silent':
    return tasks.capture(item, engine.resolve(deferralStrategy).defaultPresetId)
}

Throws for an invalid originatorTier, or a confidence outside 0–1.

DemandInput

Prop

Type

/>

DemandOutcome

Prop

Type

/>

Outcomes are frozen.

DemandAcknowledgment

Prop

Type

/>

The two axes are independent on purpose. Length comes from the level's admission config; whether the wording may be composed at all comes from autonomy. Collapsing them would make brief unreachable.

Types

OriginatorTier

type OriginatorTier = 'exempt' | 'known' | 'unknown'
  • exempt - the inner circle. Always admitted live, at every level, and never acknowledged by machine.
  • known - an established correspondent.
  • unknown - no established relationship.

Tier assignment is the app's job: a screener approval, a contacts list, an org chart. The policy only consumes the tier.

The exempt rule is also what defuses the gaming risk. An originator who learns that an acknowledgment means "deprioritised" and escalates through another channel only succeeds if their escalation is one the user cannot ignore - which is what makes them exempt in the first place.

DemandAdmission

type DemandAdmission = 'live' | 'acknowledge' | 'silent'
  • live - reaches the user now, untouched by this policy.
  • acknowledge - the originator is acknowledged and the obligation is captured for later. The two are one act: an acknowledgment without a capture is a promise nobody kept, a capture without an acknowledgment leaves the originator in silence.
  • silent - captured for later with no acknowledgment.

AcknowledgmentDetail

type AcknowledgmentDetail = 'full' | 'brief' | 'minimal'

How much the acknowledgment may say. Every variant should report system state and never intent: "received and queued, current response horizon is early next week" is a fact, while "I'll get back to you soon" is a promise the user's Tuesday self has to keep. Take the horizon from deferralStrategy so the queue and the acknowledgment cannot disagree.

DemandOutcomeReason

type DemandOutcomeReason =
  | 'exempt-originator'
  | 'tier-admitted'
  | 'no-obligation'
  | 'acknowledgment-disabled'
  | 'below-confidence'
  | 'acknowledged'

isOriginatorTier

function isOriginatorTier(value: unknown): value is OriginatorTier

Runtime validation for a tier arriving from outside the type system.

demandAdmissionStrategy

const demandAdmissionStrategy: AdaptationStrategy<DemandAdmissionConfig>

DemandAdmissionConfig

Prop

Type

/>

Values per level

LeveloriginatorThresholdacknowledgeacknowledgmentDetail
100allfalsefull
75knowntruefull
50exempttruefull
25exempttruebrief
0exempttrueminimal

At full capacity there is no policy at all: everything reaches the user. At rest the acknowledgment survives, stripped to a fixed template - the originator's social debt still clears, which is the whole point of the loop, but nothing is composed on the user's behalf.

Whether an acknowledgment actually goes out at rest depends on the app's classifier. The level permits one at certainty; a heuristic classifier reporting less than 1 will queue in silence instead, which is usually the right answer.

Disclosure is the app's job

An automated action toward a third party must be identifiable as automated. The library does not enforce this because the medium is the app's: an Auto-Submitted: auto-replied header on email (RFC 3834, which also stops two auto-responders looping), an "auto-queued" badge on a comment reply, a system-attributed status chip. Undisclosed automation speaking in a user's name is the failure mode this design exists to avoid.