Files
institution/Wiki/core/Secured-Context.md
T
flan e96ac23949 Move AI attribution to a single NOTICE file
Per the updated project policy, AI/Claude attribution lives in exactly one NOTICE file per repo and
nowhere else -- removed it from the About description, README, wiki, and roadmap.
2026-07-16 01:30:09 +00:00

6.6 KiB
Raw Blame History

Secured Context — the kind of hold a pawn is under

The suite has a rule that looks small and is load-bearing: nothing keys off "is this a prisoner." It keys off SecuredContext — the kind of hold a pawn is under. That indirection is why a ward patient, a slave, and (later) anyone inside a secure zone get the same concealment, search, schedule, and needs machinery for free, without any module hard-coding a pawn status.

SecuredContext reduces a pawn to one of three kinds and the question "who, if anyone, may search them." Everything downstream reads that, so a new module works across every context the day it is written, and a new context works across every existing module the day it is added.

Verified against Source/Core/SecuredContext.cs.


The three kinds

public enum SecuredKind
{
    Free,       // a free colonist
    Prisoner,   // a prisoner of the colony
    Slave,      // a slave (Ideology)
}
Kind Who it is May search them Can conceal? Held against their will?
Free a free colonist no one — until a secure-area or policing layer grants standing yes — they can hoard no
Prisoner a prisoner of the colony the warden yes yes
Slave a slave (Ideology DLC) the overseer yes yes

Two subtleties fall out of this table:

  • Free colonists can still conceal. A free colonist "can hoard contraband, but no one has standing to search them until a secure-area or policing layer grants it." Freedom is not innocence — it is only the absence of an authority permitted to check. That is why CanConceal (below) includes the free.
  • Prisoner and Slave differ only in who holds the keys. Both are "held"; the search authority is the warden for one and the overseer for the other. Downstream modules that only care "is someone entitled to search this pawn" read IsHeld and never branch on which.

The struct

SecuredContext is a small readonly struct — the kind, the pawn, and two computed predicates.

public readonly struct SecuredContext
{
    public readonly SecuredKind Kind;
    public readonly Pawn Pawn;

    // Held against their will -- prisoner or slave. Excludes a free colonist.
    public bool IsHeld    => Kind == SecuredKind.Prisoner || Kind == SecuredKind.Slave;

    // Anyone with something to hide -- includes free colonists (who may hoard).
    public bool CanConceal => Pawn != null;
}

The two predicates split the world along two different axes, and picking the right one is the whole skill of using this type:

Predicate True for Ask it when you care about…
IsHeld Prisoner, Slave custody — who is under the colony's control, who can be disciplined, whose cell can be searched
CanConceal Free, Prisoner, Slave (any real pawn) contraband — who could be hiding something, regardless of status

For example, Propensity's Nurture uses IsHeld in its "held & unhappy compounds" clause — it only wants to pile the ×1.4 penalty on a pawn the colony actually holds and mistreats, not on a grumpy free colonist. A warden-search feature, by contrast, would gate on CanConceal to decide who is even worth checking, and then on standing to decide whether it is allowed to.


Of — one pawn to a context

public static SecuredContext? Of(Pawn p)

Of maps a pawn to a context, or null if the pawn has none. The order of the checks matters and is worth reading literally:

if p is not humanlike, or p is dead        -> null      (not one of ours)
if p.IsPrisonerOfColony                     -> Prisoner
if p.IsSlaveOfColony                        -> Slave
if p.IsFreeColonist                         -> Free
otherwise                                   -> null
  • The guard comes first. Animals, mechs, and the dead are simply not the suite's business — they return null, and every OnMap loop and downstream check skips them without a special case.
  • Prisoner and Slave are tested before Free. A pawn who is somehow both a colonist and imprisoned is classified by their hold first. The nullable return is the clean signal for "this pawn is outside the model," so callers pattern-match HasValue rather than defaulting to some "Free-ish" status.

Returning SecuredContext? (nullable) rather than a Free fallback is deliberate: there is a real difference between "a free pawn we track" and "a pawn we do not model at all," and collapsing them would let non-colony pawns leak into colony machinery.

OnMap — every context on a map

public static IEnumerable<SecuredContext> OnMap(Map map)

OnMap yields a context for every spawned pawn that has one — prisoners, slaves, and colonists, skipping everything Of returns null for. It is a lazy yield iterator, so a caller can enumerate "everyone the suite cares about on this map" in one loop:

foreach (SecuredContext ctx in SecuredContexts.OnMap(map))
{
    if (ctx.IsHeld) { /* consider only the held */ }
}

A null map yields nothing (an empty sequence), so callers do not need to null-check before looping.


Why not just "prisoner"?

This is the design decision the whole page exists to justify, so it is worth stating plainly. If every module checked pawn.IsPrisonerOfColony directly, then:

  • adding slaves to a feature would mean editing every module;
  • adding a ward patient mode would mean editing every module;
  • adding a future secure-zone concept would mean editing every module.

By routing all of them through one predicate, a new hold-kind is added once, in Core, and every existing module inherits it. The concealment, search, and needs machinery never has to learn what a slave is — it only ever asked IsHeld and CanConceal.

The deliberately-absent Ward kind

There is no SecuredKind.WardPatient, and its absence is intentional. From the source:

"a committed patient IS a prisoner in vanilla's sense (Ward rides the prisoner rail), so it is SecuredKind.Prisoner here and needs no special case. Contraband does not depend on Ward."

Because Institution: Ward implements a committed patient on top of vanilla's prisoner rail, such a pawn already returns Prisoner from Of — no new kind, no dependency from Core (or Contraband) onto Ward, and every module treats a ward patient exactly as it treats any prisoner. The right amount of new code to support Ward's hold semantics was zero, and that is the payoff of keying off the context instead of the label.


Part of the Institution suite.