A standard library for claims that might be wrong. Three-valued verdicts where
"not enough to say" is a first-class answer — not a False in disguise.
Pure Python standard library. No dependencies.
pip install krisis
python3 -m krisis # the witness, including the litmus testκρίσις — discernment; the root of both crisis and criterion.
Most software's default answer is "yes." A match is returned, a threshold fires, a value is asserted. That default is a lie whenever the real answer is "the question is ill-posed." A binary system can't say that — it has two boxes, and the honest answer needs a third.
from krisis import Trit, Verdict, tnot, tand
tnot(Trit.ZERO) is Trit.ZERO # True — the law that proves this isn't binary
bool(Verdict.unknown("no data")) # raises — ZERO can never silently become FalseTwo things get claimed. Only one survives a probe.
The trap — "ternary hardware is faster." Radix economy peaks at base e ≈ 2.718, so base 3 beats base 2 by 5.4% in representation cost. Real, tiny, and reclaimed by binary on physical two-state transistors. Church–Turing: neither base computes anything the other can't. This claim is false, and we say so.
The prize — "three-valued logic refuses to answer a bad question." A two-valued system maps every input to true or false, so an ill-posed input gets a confident wrong answer that every later stage runs on. Ternary returns ZERO and stops. Measured on a six-stage pipeline, 30% ill-posed inputs:
| system | compute units | wrong answers |
|---|---|---|
| binary | 60,000 | 3,015 |
| ternary | 44,925 | 0 |
25% of compute saved, every wrong answer prevented. The win is avoided work plus avoided error — semantic, not hardware. That's the honest pitch, and it's a large one.
The failure mode of every fake three-valued system: ZERO secretly collapses to true or false somewhere. The litmus test is negation.
In any two-valued system NOT flips a bit. A fixed point of NOT — a value that is neither true nor false — cannot be embedded in {T, F}.
tnot(ZERO) is ZERO ← the tell. If this fails, you built binary.
tand(ZERO, ZERO) is ZERO ← not False
tand(PLUS, MINUS) is MINUS ← one refutation kills a conjunction
tand(PLUS, ZERO) is ZERO ← but ignorance leaves it open, never kills
Trit.of(None) is ZERO ← None is unread, not false (the classic leak)
Verdict.__bool__ raises ← no accidental collapse, ever
Verdict has one loud escape hatch — .decided() — for the boundaries where you
genuinely must go two-valued (a CLI exit code). It's a deliberate call, never an
implicit one, so the collapse can't happen by accident.
from krisis import consensus
consensus([PLUS, PLUS, MINUS, ZERO, ZERO]) # (PLUS, net=+1, unknowns=2)
consensus([ZERO, ZERO, ZERO, ZERO]) # (ZERO, ...) — not a false majorityAbstentions are counted and reported, never folded into either side. A mostly undecided room returns ZERO, which is the difference between honest aggregation and a push poll.
krisis.logic is the first real thing built on the core: a fallacy engine that is
right precisely because it is ternary. It holds three questions apart that a
binary flagger collapses into one — and the separation is the whole correctness.
1. Is the form valid? — decidable, exactly.
from krisis import assess_validity, Var, Implies
p, q = Var("p"), Var("q")
assess_validity([Implies(p, q), q], p) # affirming the consequent
# MINUS — invalid; countermodel {p: False, q: True}A truth table settles every propositional argument, and on failure hands back the countermodel — the exact world where the premises hold and the conclusion fails. That's a disproof you can check by hand, not an opinion. Here validity is never ZERO: it's decidable, so the engine commits.
2. Is it a fallacy? — for informal ones, ZERO, with the content-question named.
from krisis import INFORMAL
INFORMAL["appeal_to_authority"].flag()
# ZERO — "has the shape of appeal to authority; whether it is fallacious depends
# on content this engine cannot see — is X a legitimate expert on P?""A cardiologist says statins lower LDL" and "…says the vaccine causes autism" have the same shape and opposite verdicts, decided by content a parser can't see. Asserting MINUS from form alone would itself be a fallacy — so the engine flags the shape and returns ZERO, naming exactly what a human must answer to resolve it.
3. Is the conclusion true? — never inferred from the argument.
from krisis import conclusion_truth, assess_validity
conclusion_truth(assess_validity(bad_premises, conclusion))
# ZERO — "a bad argument for P does not make P false — that would be the fallacy fallacy"This is the guard that makes it honest. A bad argument for P doesn't make P false;
a valid argument only transmits truth if its premises are true. conclusion_truth
always returns ZERO from validity alone, so the engine cannot commit the
argument-from-fallacy — the single most common error in automated reasoning about
arguments.
diagnose() returns all three at once, kept separate:
validity MINUS invalid — countermodel {p: False, q: True}
fallacy ZERO shape of appeal to authority; needs: is X a real expert?
conclusion_truth ZERO a bad argument does not make the claim false
Three questions, three verdicts, none collapsed into another. That is the discipline a binary flagger cannot hold — and the reason this had to be ternary.
krisis.cipher is the third module, and it earns its place by fixing a real bug
in the code it was extracted from. A cipher has three operations, and only one is
hard:
scramble(text, key) # apply a key — trivial, always PLUS
unscramble(text, key) # apply the inverse — trivial, always PLUS
crack(ciphertext) # RECOVER the key — a search, and often ZEROCracking is constraint satisfaction over an unknown key, and its verdict is three-valued exactly the way validity is:
from krisis import crack_multiplicative
# "number = letter × location" cipher — recover the location permutation
crack_multiplicative([8, 10, 36, 48, 75]) # HELLO's numbers
# ZERO — "underdetermined — 8 keys fit (HELLO, HEIPO, HBRLY, …)"The source of this idea returned HRYLB for those numbers — one of eight valid answers, presented as the answer. That's the two-valued lie in miniature: a confident reply to an underdetermined question. krisis returns ZERO and the count of keys that fit as the reason.
- PLUS — the key is unique, and the recovered key is the proof (just as a countermodel proves an invalid form).
- ZERO — more than one key fits; underdetermined, with the count.
- MINUS — no key fits; the ciphertext contradicts the scheme.
The lesson, banked from the source: when a map implies a constraint, solve the constraint — never let a weak statistic outvote structure. A frequency fit that barely beats noise is not a key, the same way similarity is not evidence.
krisis.systole (συστολή, the heart's contraction) is compression done as a
bounded search for structure, three-valued and honest at the floor.
The floor is a counting argument, and it protects your data. There are 2ⁿ files of length n and only 2ⁿ−1 shorter ones, so at least one file cannot shrink (pigeonhole). A scheme that shrank everything would map two files to one code — and then couldn't give either back. The floor is what makes decompression work.
So the engine never claims to beat it. On truly random data it returns MINUS and stores verbatim — an honest 0%, never a corrupting lie:
from krisis import compress, decompress
compress(bytes(100_000)) # PLUS — 100000→~120 bytes, entropy 0.0
compress(os.urandom(100_000))# MINUS — incompressible, stored verbatim (round-trips)But "negative space" is real for structure — you change the problem, not the theorem:
from krisis import find_generator, regenerate, Dedup, delta, patch
# GENERATE — store the program, not the output
g = find_generator(bytes(1_000_000)) # a ~30-byte generator for a million bytes
regenerate(g.value) == bytes(1_000_000) # True — exact
# DEDUP — a duplicate costs a pointer, not the bytes (negative MARGINAL space)
store = Dedup(); store.put(big); store.put(big) # second copy adds zero storage
# DELTA — send only what the receiver lacks
patch(base, delta(base, target)) == target # a one-byte change costs one editEach "floats past" the naive per-file floor by using information the file didn't contain in isolation — another copy existed, a generator existed, a reference existed. That's the manhole-cover inversion done licitly, and it's where the world's data compression actually lives. K is uncomputable, so it's a bounded search, and the verdict is three-valued: structure found (PLUS), at the floor (MINUS), or budget exhausted (ZERO).
The system is a body: organs held in balance by regulating systems. Most systems
already have an analog; krisis.homeostasis adds the one that was missing — the
endocrine/setpoint regulator that closes the feedback loop.
| body system | analog | where |
|---|---|---|
| skeleton (structure, invariants) | Trit/Verdict spine, the canon (dogma) |
trit, synod |
| nervous (sense + reflex) | interoception, reflex arcs; discernment | brightchain.autonomic, krisis |
| endocrine / homeostasis (setpoint regulation) | negative-feedback vitals | homeostasis |
| immune (defend, refuse foreign) | adversarial suite, the refuses discipline |
brightchain.redteam, synod |
| regulatory (govern change) | conciliar self-repair | synod |
| muscular (action under control) | apply within the boundary; transforms | synod, diaskeue |
| digestive (ingest, extract) | content-address + tag; tokenize; compress | diaskeue.atelier, glossa, systole |
| circulatory (transport value) | multilateral netting | koinonia.netting |
| excretory (purge waste) | deletion, self-pruning to budget | brightchain.tombstone, condense |
| reproductive (propagate) | content-addressed reproducible recipes | diaskeue.registry |
| integumentary (barrier) | firewall levels, the API surface | brightchain, package boundaries |
Homeostasis closes the loop that brightchain.autonomic only opened — it senses
and corrects:
from krisis import Setpoint, Vital, body_tone, homeostat
temp = Setpoint("temp", target=37.0, tolerance=0.5, hard_low=35, hard_high=40)
Vital(38.5, temp).state # ZERO — drifting (correctable)
Vital(41.0, temp).state # MINUS — critical (past the hard bound)
homeostat(39.0, temp, gain=0.5) # PLUS — negative feedback stabilized it into band
homeostat(39.0, temp, gain=2.5) # MINUS — unstable gain diverges, and says sobody_tone aggregates vitals by Kleene AND — one critical vital fails the whole
body; five healthy do not outvote one that flatlined. And the self-maintenance loop
is now complete: homeostasis senses → krisis discerns → synod governs and
acts → homeostasis re-senses (reception). The nervous system reflexes, the
endocrine system regulates, the synod heals.
krisis.synod (σύνοδος, a council) lets the system repair itself without ever
acting as a lone cell on its own aggregate calculus. The boundary comes from two
places that agree — how the Orthodox Church governs, and how a healthy cell does.
The danger, and the correction. "Utilitarian benefit of the whole" has a reading that must be refused: raw aggregate utilitarianism sacrifices a healthy member to raise the total — the tumour's logic. The correction is the Body — "if one member suffers, all suffer together" (1 Cor 12:26). What licenses autonomous action is not the aggregate but Pareto: helps the whole, harms no part.
Three tiers:
| tier | what | rule |
|---|---|---|
| DOGMA | the founding law, the human gate, the safety guards | never auto-touched — convene returns MINUS |
| ECONOMIA | a strict Pareto improvement | autonomous — the cell heals itself |
| SYNOD | a net-good change with a cost | human-gated — escalated, never auto-applied |
from krisis import convene, Repair
res = convene(repair, before=health, measure=remeasure, verdicts=krisis_judgments, canon={"law"})
res.verdict.tag # PLUS applied+kept · ZERO withheld/escalated · MINUS forbidden- Quorum, not a lone signal — the verdicts are combined by summing log-odds
(
graded); a single or split signal never authorizes a change. - Reception — every authorized repair is applied provisionally, health is re-measured, and anything that didn't actually heal is reverted. The cell-cycle checkpoint; the council's decree validated by the body's Amen.
- The human keeps the canon — defines what is dogma, vetoes any tier, and alone authorizes a synod-tier trade-off. The system heals the economia; nothing else.
krisis.game is discernment among many agents with conflicting interests — the
debt engine's missing math. It turns "optimal if people are faithful" into
"designed so that faithfulness pays," and stays three-valued: a strategy provably
dominates (PLUS), is dominated (MINUS), or neither (ZERO — needs mixing).
The dilemma is real — in the prisoner's dilemma, defection strictly dominates, so the only equilibrium is (defect, defect) and it's Pareto-dominated. That's why a lending circle defaults without structure; good intentions don't repeal it:
from krisis import prisoners_dilemma, is_dilemma, dominant_strategy
is_dilemma(prisoners_dilemma()).tag # PLUS — the Nash is Pareto-dominatedThe folk theorem makes faith a threshold. Repeat the game and cooperation is
sustainable iff the discount factor δ (how much you value the future) clears
(T−R)/(T−P):
from krisis import cooperation_threshold, sustainable
cooperation_threshold(5, 3, 1, 0) # 0.5
sustainable(5, 3, 1, 0, delta=0.9).tag # PLUS — a patient community holds the commons
sustainable(5, 3, 1, 0, delta=0.2).tag # MINUS — too myopic, defection winsFaith is the high δ that clears the threshold — "faithfulness sustains the commons" as a theorem, not a sentiment.
Mechanism design engineers honesty into the dominant strategy. Add a defection
penalty k > max(T−R, P−S) — a sealed, unforgeable reputation cost — and
cooperation becomes self-enforcing, no faith required:
from krisis import min_penalty, incentive_compatible
min_penalty(5, 3, 1, 0) # 2.0
incentive_compatible(5, 3, 1, 0, penalty=3).tag # PLUS — honesty dominant
incentive_compatible(5, 3, 1, 0, penalty=1).tag # MINUS — gameable, it will be exploitedA brightchain reputation seal is that k. This is how the debt engine makes
faithful cooperation a designed equilibrium.
krisis.peirce is the lateral half of discernment. Where logic decides
validity (deduction — the vertical judge), this proposes: it generates and
ranks explanations, classifies signs by how they mean, and cuts idle distinctions.
Peirce founded semiotics and American pragmatism and triadic logic — so a
semiotic engine is his, and it formalizes rigorously.
The sign triad is a measurable 2-D space:
from krisis import classify_sign
classify_sign(0.9, 0.9).value # SignType.ICON — resembles (a portrait)
classify_sign(0.1, 0.95).value # SignType.INDEX — correlated (smoke→fire)
classify_sign(0.05, 0.1).value # SignType.SYMBOL — arbitrary (the word "dog")
classify_sign(0.55, 0.5).tag # ZERO — onomatopoeia is honestly mixedAbduction is the lateral leap — Bayesian, and three-valued:
from krisis import abduce, Hypothesis
abduce([Hypothesis("it rained", 0.8, 0.3), Hypothesis("a comet melted", 0.99, 0.001)])
# the comet explains PERFECTLY and ranks last — rejected by its prior, not its fitSUPPORTED (a clear best) · UNDETERMINED (a tie — abduction proposes, never settles,
the same many-keys ZERO as a cipher) · REFUTED (nothing explains it). And it rides
the graded log-odds line, so a supported abduction carries a score you can combine.
The pragmatic blade cuts a difference that makes no difference:
from krisis import pragmatic_blade
pragmatic_blade(("moves 3","ticks 5"), ("moves 3","ticks 5")).tag # PLUS — idle, cut it
pragmatic_blade(("moves 3","ticks 5"), ("moves 3","ticks 4")).tag # MINUS — real differenceThe cycle of inquiry is the whole family: ABDUCE (peirce, the lateral leap)
→ DEDUCE (logic, trace consequences) → INDUCE (assay, test against chance).
Propose laterally, prove necessarily, test statistically.
krisis.graded is the continuous form of the trit: a score on the whole real line
whose sign is the qutrit and whose magnitude is distance toward a telos.
−∞ Mal dissolution (a limit) 0 Neu where the work is done +∞ Eu the Good (a limit)
Three claims, each a theorem, not a metaphor:
- The bulk of work is at zero. The effect of one unit of evidence is
p·(1−p), maximal (0.25) at Neu and vanishing toward either telos. Neutrality is exactly where work is cheapest and most consequential — the peak of the sigmoid. - The two ternaries are one.
tanhmaps the line to the bounded{−1, 0, +1}:tanh(±∞) = ±1. The qutrit and the infinities are one object at two zooms — and the bounded form is the one to work in (smallest numbers, easiest to manage). - Stacking evidence is adding log-odds — exact Bayes.
combinesums verdicts; three witnesses for Eu and one for Mal don't vote, they sum.
from krisis import Graded, combine, work_at
combine(Graded(1.2), Graded(0.8), Graded(-0.5), Graded(2.0)).trit # Trit.PLUS (+3.5)
work_at(0.0) # 0.25 — the peak
Graded(math.inf) # refused — a telos is approached, never heldThe two counterfeits, caught not laundered. Neu (0) is honest neutrality. Its imposters are refused:
- NULL =
NaN— the counterfeit of neutrality. It cannot affirm itself (NaN != NaN) and poisons what it touches. Refused, never read as an honest zero — that laundering is the danger. - VOID =
None— obliteration, no value at all. Distinct from a real zero. - Externality = a score past the working horizon (~36), where float saturates the scale. Noticed, not computed with as if precise. Keep the numbers small.
krisis.schumacher implements the genuine quantum-compression result: Schumacher's
theorem (1995), the quantum sibling of Shannon. A source of density matrix ρ
compresses to S(ρ) = −Tr(ρ log₂ ρ) qubits per symbol, and no fewer.
from krisis import von_neumann_entropy, compressibility, QUTRIT_MAX_BITS
von_neumann_entropy([1.0, 0.0]) # 0.0 — a pure state condenses to nothing
von_neumann_entropy([0.5, 0.5]) # 1.0 — a maximally mixed qubit, incompressible
compressibility([0.9, 0.1]) # PLUS — real saving below the ceiling
QUTRIT_MAX_BITS # 1.585 — the {−1,0,1} qutrit outcarries a qubit- S(ρ) ≥ 0 always — never negative. A pure state reaches exactly 0 (total knowledge, unity); you cannot pass below. The floor is real, and it's at zero.
- The hologram is real —
holographic_bits(area)gives the Bekenstein/area-law ceiling: a region's information is bounded by its surface area, not its volume. The deepest compression statement in physics, honored not beaten. - Landauer —
landauer_joules(bits)is the energy floor to erase: information can't be destroyed for free, so it's conserved and only moves.
The verdict is three-valued like everything else: compressible (PLUS), at the
incompressible ceiling (MINUS), or barely worth it (ZERO). The quantum floor is
what makes the compression reversible — the same lesson as systole at the
classical floor.
This isn't novel for its own sake — it's a well-worn idea most languages skip.
SQL has three-valued logic (NULL, IS UNKNOWN). Rust has Option and
no null. Kleene and Łukasiewicz formalised it in the 1920s–30s. krisis is
that lesson as a tiny importable discipline for the space between — findings,
measurements, verdicts — where a confident false answer is worse than an honest
ZERO.
trit (22), logic (26), cipher (20), assay (14), systole (30),
schumacher (27), graded (33), peirce (21), game (24), synod (20), and
homeostasis (setpoint regulation, 20 checks) are built — 257 checks, eleven modules. It's the founding core of a family of eight domain
packages (finance, genomics, linguistics, provenance, time) that each reimplemented
this stance — see SPEC.md for the full design and the extraction plan:
the null-model harness (assay), the recursion-breaker (bound), and the witness
harness follow, each lifted from a package that already proved it in production.
- Not a ternary VM. The prize is semantic; emulating trit hardware chases the 5.4% trap and adds nothing.
- Not a logic-programming system. Kleene evaluation, not inference.
- Not a framework. Pure standard library — the discipline costs nothing to adopt, which is the whole point.