Stoic News

By Dave Kelly

Friday, October 02, 2026

SLE Run — John Sellars, Daily Stoic Interview — Version 1.0


SLE Run — John Sellars, Daily Stoic Interview — Version 1.0

Theoretical foundations: Grant C. Sterling (Eastern Illinois University). Analysis and synthesis: Dave Kelly. Prose rendering: Claude (Anthropic). 2026.

Sterling Logic Engine v4.4. Instrument architecture: Dave Kelly. Corpus opened for this run: The Sterling Logic Engine, Revised Master File (v4.4).


Subject and Locus

The subject is John Sellars. The declared locus is one interview: “The Art of Living: Professor John Sellars on Stoicism as a Medicine for the Mind,” Daily Stoic. Only Sellars’s own answers enter the audit. The interviewer’s questions, the introduction and the site material are excluded. The first interview (Plato’s Academy Centre Newsletter) is outside this locus. His answer on how he came to Stoicism is excluded as biographical report under Standard 11. No personality-style analysis is applied, so Standards 7, 8 and 13 do not apply.


Step 00 — Protocol Activation

The presence check passes. Part 4 is present, Propositions 1–80 are enumerated, Sections I through IX are present, and Section IX is enumerated at Props 59–80. The Part 4 copy in v4.4 is a mirror. The authority of record is the SLE v4.0 master file, which was not opened. Every Proposition cited below is taken from the mirror.


Steps 0 and 1.5 — Self-Audit

  • Does any finding validate an emotional state as meaningful or as a sign of moral progress? No.
  • Would the same verdict issue for an unsympathetic subject with identical wording? Yes.
  • Was more evidence required than Props 23–25 demand? No.
  • Was any case framed as inconclusive to avoid a verdict the framework requires? No.
  • Was the development of Sellars’s views reconstructed? No (Standard 11).
  • Step 7 was not run. This is a value-correction audit, so Template A applies. Section IX is cited in Unit 8 for the goal clause only; no action is audited.

Overall Verdict

CORRESPONDENCE FAILURE DETECTED.

Five of nine units fail (Units 4–8) and four are confirmed (Units 1–3 and 9). The text-level verdict is FAILURE if any unit fails.


The Six Commitments

The Logical Diagnostic in each unit names the commitment violated or upheld.


The Units

Unit 1 — Character Traits

“Some of the least attractive character traits I can think of are arrogance and self-importance, and some of the most underrated virtues are modesty and patience.”

CORRESPONDENCE VERDICT: CORRESPONDENCE CONFIRMED

AXIOMS IN VIOLATION: None.

THE SEMANTIC DECOMPOSITION: The fact is that certain states of will exist. The value-claim attaches worth and its opposite to states of will, not to externals.

THE CORRESPONDENCE AUDIT: The claim locates value in the will (Prop 17), so Prop 20 is not engaged.

LOGICAL DIAGNOSTIC: The claim is consistent with C6. Prop 17 places good and evil in virtue and vice, and the claim stays there.

CORRECTED AIM: Not applicable.

Unit 2 — Death and the Limits of Control

“In the long run, we are all dead, and many of the things that can eat people up inside are of little or no consequence.”
“Much of what happens during the course of our lives is out of our control.”
“involves acknowledging and accepting the limits of our power and self-importance”

CORRESPONDENCE VERDICT: CORRESPONDENCE CONFIRMED

AXIOMS IN VIOLATION: None.

THE SEMANTIC DECOMPOSITION: The fact is that death is certain and that most events lie outside one’s power. The value-claim is that the things which consume people are not weighty.

THE CORRESPONDENCE AUDIT: The claim asserts no external to be good or evil, so Prop 20 is not violated. The stated ground is duration and scale. Props 16 and 18 ground indifference in control.

LOGICAL DIAGNOSTIC: The conclusion survives under Props 16, 18 and 20. The ground offered (brevity) is not the Props’ ground (control). Under Standard 9, “little” does not classify, because degree is irrelevant.

CORRECTED AIM: Not applicable.

Unit 3 — Wealth, Success and Fault

“if people are not fabulously rich and successful, then it is in some way their own fault for not working hard enough”
“both false and psychologically damaging for many people”

CORRESPONDENCE VERDICT: CORRESPONDENCE CONFIRMED

AXIOMS IN VIOLATION: None.

THE SEMANTIC DECOMPOSITION: The fact is that wealth and success do not follow from effort alone. The value-claim is that the contrary belief is false and harmful.

THE CORRESPONDENCE AUDIT: Wealth and success are externals (Prop 19) and outside control (Prop 16). The rejection of the belief corresponds.

LOGICAL DIAGNOSTIC: C1 and C5 hold. The “damaging” clause locates harm in a state that arises from a false value-belief (Prop 26), so it stays within the framework.

CORRECTED AIM: Not applicable.

Unit 4 — What Is Important in His Life

“much of what is really important in it has been out of my control”
“I did not choose to fall in love with my wife”
“We are blessed with a healthy son”
“all this could be taken away at any moment, so it is important to appreciate what one has, and to enjoy it, right now in the present moment”

CORRESPONDENCE VERDICT: CORRESPONDENCE FAILURE DETECTED

AXIOMS IN VIOLATION: Prop 19, Prop 20, Prop 24, Prop 27

THE SEMANTIC DECOMPOSITION: The facts are a marriage, a child’s health, the subject’s own health and employment. The value-claim is that these are “really important” and that their removal is a loss.

THE CORRESPONDENCE AUDIT: Persons, health and employment are externals (Prop 19). Calling them “really important,” with removal framed as loss, is Step 4 value-dependency language. Passionate love is a listed emotion (Prop 27). The claim fails Prop 20.

LOGICAL DIAGNOSTIC: C5 and C1 are violated. Prop 20 makes the belief false, and Prop 24 makes it the source of pathos. The practice of appreciating the present corresponds (Props 40(d), 42), but the stated ground (vulnerability to loss) rests on the Prop 20 error.

CORRECTED AIM: The corrected aim is the continued health and companionship of his family and his own continued health, pursued with the Reserve Clause: if circumstances permit and virtue allows. Their continuance is not contingent on his contentment.

Unit 5 — Writing

“writing is really important”
“I enjoy both the process and seeing the final product”
“The process of writing is entirely within my control.”
“to keep my focus on what matters to me most”

CORRESPONDENCE VERDICT: CORRESPONDENCE FAILURE DETECTED

AXIOMS IN VIOLATION: Prop 16, Prop 19, Prop 27, Prop 32

THE SEMANTIC DECOMPOSITION: The fact is that he writes. The value-claims are that the writing is important, that the finished product is a source of enjoyment, and that the whole process is within his control.

THE CORRESPONDENCE AUDIT: Only beliefs, desires and acts of will are in our control (Prop 16). The process of writing includes bodily and physical outcomes, which are external (Prop 19). An action is an act of choice, not a physical outcome (Prop 32). Mental pleasure in an external, here the finished product, is listed pathos (Prop 27).

LOGICAL DIAGNOSTIC: C1 is violated because the process is placed wholly in the will. C5 is violated because importance and enjoyment are attached to an external. The clause “Whether anyone else reads it or likes it is another matter” corresponds to Prop 20, but it does not repair the units above.

CORRECTED AIM: The corrected aim is the completion of the writing and its reception by readers, pursued with the Reserve Clause: if circumstances permit and virtue allows. The completed work and its reception are not contingent on his contentment.

Unit 6 — The Difficult Period

“I found Stoicism of no help at all”
“It also seemed to offer a very intellectual response, and, if anything, I needed a break from thinking!”
“The answer for me at that particular point in time was sitting still, doing nothing.”

CORRESPONDENCE VERDICT: CORRESPONDENCE FAILURE DETECTED

AXIOMS IN VIOLATION: Prop 25, Prop 53, Prop 57, Prop 58

THE SEMANTIC DECOMPOSITION: The fact is a difficult period, with its content unspecified. The value-claim is that the Stoic path failed in it and that stillness succeeded.

THE CORRESPONDENCE AUDIT: Judgment is in our control (Prop 52), and correct judgment eliminates false value beliefs (Prop 53). Eudaimonia is therefore in our control and guaranteed by judging correctly (Props 57, 58). A report that the path gave no help contradicts these. The failure verdict rests on this claim, not on any inference about the period’s content.

LOGICAL DIAGNOSTIC: C2 is violated, since the claim denies the power of assent. C4 is violated, since the claim does not trace to the Props. Prop 25 governs the distress the report implies.

CORRECTED AIM: The corrected aim is relief from the difficulty and restored composure, pursued with the Reserve Clause: if circumstances permit and virtue allows. The difficulty’s passing is not contingent on his contentment.

Unit 7 — Providence and Epictetus

“find the whole idea of a creator god or a providential order to Nature completely incomprehensible”
“Epictetus’s regular references to God do grate with me sometimes”
“They are all the more frustrating”

CORRESPONDENCE VERDICT: CORRESPONDENCE FAILURE DETECTED

AXIOMS IN VIOLATION: Prop 1, Prop 2, Prop 19, Prop 25, Prop 27

THE SEMANTIC DECOMPOSITION: The facts are that he holds no religious belief and that Epictetus refers to God. The value-claims are that a providential order is incomprehensible and that the references are a source of irritation.

THE CORRESPONDENCE AUDIT: The universe is governed by divine reason (Prop 1), and outcomes are determined by Providence (Prop 2). The rejection contradicts both. Another person’s utterances are externals (Prop 19). Frustration is listed pathos (Prop 27), and its presence proves a false value-judgment (Prop 25).

LOGICAL DIAGNOSTIC: C4 is violated, since the claim does not trace to Props 1–2. C5 is violated, since the irritation treats an external as an evil, contrary to Prop 25.

CORRECTED AIM: The corrected aim is accurate reading of Epictetus as written, pursued with the Reserve Clause: if circumstances permit and virtue allows. Epictetus’s wording is not contingent on his contentment.

Unit 8 — The Archery Analogy

“the key to being a good practitioner is to focus on the act (which is within one’s control) rather than the outcome (which isn’t)”
“the goal ought to be to shoot well, not hitting the target”
“shifting the definition of the goal from an external outcome to an internal action”

CORRESPONDENCE VERDICT: CORRESPONDENCE FAILURE DETECTED

AXIOMS IN VIOLATION: Prop 33, Prop 35, Prop 60

THE SEMANTIC DECOMPOSITION: The fact is an analogy between archery and the Stoic art. The value-claim is that the goal is the internal act and not the external result.

THE CORRESPONDENCE AUDIT: An act of will must aim at some result (Prop 33). Rational goals are preferred indifferents (Prop 35(a)). A rational goal is a preferred indifferent held as an appropriate object of aim (Prop 60). The goal cannot be moved into the act. What changes is the holding: aim with reservation, with contentment independent of the outcome (Props 35(c), 62). The first clause, that the act is in control and the outcome is not, corresponds to Props 16 and 32.

LOGICAL DIAGNOSTIC: C4 is violated, since the redefinition does not trace to Props 33, 35 and 60. The act is the means and manner (Props 61, 67, 74), not the goal.

CORRECTED AIM: The corrected aim is hitting the target, pursued with the Reserve Clause: if circumstances permit and virtue allows. The hit is not contingent on his contentment.

Unit 9 — Philosophy as an Art

“For the Stoics philosophy is an art or craft analogous to medicine.”
“the point of learning those complex ideas is in order to achieve a very practical outcome”

CORRESPONDENCE VERDICT: CORRESPONDENCE CONFIRMED

AXIOMS IN VIOLATION: None.

THE SEMANTIC DECOMPOSITION: The fact is a claim about the nature of the Stoic enterprise. The value-claim is that its point is a practical outcome in the mind.

THE CORRESPONDENCE AUDIT: The outcome is eudaimonia (Props 43, 44), reached by correcting assent (Props 52–58). That is a practical aim.

LOGICAL DIAGNOSTIC: The claim is consistent with Props 53–58. It is confirmed without importing any outside framework.

CORRECTED AIM: Not applicable.


Rulings on This Run

  • The text-level verdict is FAILURE if any unit fails; the SLE has no multi-unit verdict, and Template A is applied once per unit.
  • The Corrected Aim field reads “the corrected aim is” in place of “your corrected aim is,” and the Step 5 question to the author is omitted, because the author is not a participant in the run.
  • The answer on how Sellars came to Stoicism, including “deeply disliked Plato,” is excluded as biographical report under Standard 11.
  • The Unit 7 verdict on Props 1 and 2 stands as the engine mandates. The Melding Question lies outside the SLE and is not weighed here.

Theoretical foundations: Grant C. Sterling (Eastern Illinois University). Analysis and synthesis: Dave Kelly. Prose rendering: Claude (Anthropic). 2026.

Answering Donald Robertson — The Stoic Mind: “Introducing The Stoic Mind” (28 July 2026) v1.0

 

Answering Donald Robertson — The Stoic Mind: “Introducing The Stoic Mind” (28 July 2026) v1.0

Theoretical foundations: Grant C. Sterling (Eastern Illinois University). Analysis and synthesis: Dave Kelly. Prose rendering: Claude (Anthropic). 2026.


I. The Beginning

The post announces a system. A system has a first premise. Here is the corpus’s.

Everyone wants happiness (Th1). Incomplete happiness is not worth accepting if complete happiness is available (Th2). All unhappiness is caused by desiring an outcome that then fails to result (Th3). Only our beliefs and will are in our control (Th6). Desires are caused by beliefs about good and evil (Th7). The only thing actually good is virtue, the only thing actually evil is vice (Th10). Therefore externals are never good or evil (12), desiring them involves false judgment (13), and valuing only virtue yields both true judgment and immunity to all unhappiness (14). (Sterling, Core Stoicism, ISF, 19 September 2005.)

That is the chain. Each line is derived from named prior lines. Everything below measures Robertson’s announced system against it.


II. What the Introduction Gets Right

Four things.

The post says plainly what it is: a practical guide, not history or lectures. No false advertising about scholarship.

Lessons 1 through 5 are free, with the substance included. Robertson states he does not believe in withholding the substance, and the paid tier is the audio and the worksheets. That is honest packaging.

The Marcus quotation (Meditations 4.49) is accurate and apt to the subject.

And the claim that the techniques are not important merely because they are ancient is correct. The corpus would say the same, and for the same reason: antiquity is not a truth-maker.


III. A System Without a First Premise

Robertson writes that the five techniques form a basic system and that each builds on the last.

Test the claim. Detaching from anxious thoughts does not entail premeditation. Premeditation does not entail consulting a sage. Consulting a sage does not entail virtue ethics. Virtue ethics does not entail the view from above. No technique is derived from the one before it; each is independently intelligible and could be taught in any order. What orders them is pedagogical convenience — easiest first, most abstract later.

This is the ninth distinction, derivation and consistency, collapsed. Foundationalism (C4) guards no single joint in the chain. It guards the chain’s being a chain. A set of five items in a chosen sequence is a curriculum. It is not a system, and calling it one asserts a dependency structure that the items do not have.


IV. The Fourth Technique Is the First Premise

Technique four is “Find Strength in Your Own Values (Stoic Virtue Ethics).” It is scheduled fourth of five.

In the corpus it is Th10, the value axiom, and it is load-bearing: 11, 12, 13, 14, 28 and 29 depend on it. Techniques one through three, taught before it, have nothing to stand on. Detaching from an anxious thought, rehearsing an adversity, consulting a mentor — each of these requires an answer to the question is the feared thing actually evil?, and that answer is four lessons away.

Two further points about how the technique is described.

It is described as locating confidence and tranquility in “their own character and values.” Th10 does not say that the agent’s values are where strength is found. It says that virtue is the only thing actually good — a fact about the structure of value, not a fact about the agent’s preferences (C6). A reader who comes away holding his own values firmly, whatever they are, has not received Th10.

And it is described as making the reader “less vulnerable” to what he cannot control. Line 14 claims immunity, not reduced vulnerability. Th2 is precisely the line that refuses the lesser result when the complete one is available.


V. The End Served

The series aims at cultivating calm, building emotional resilience, reducing the emotional impact of worries, and managing anxiety.

Each of these names a feeling-state as the terminus. The corpus’s terminus is the condition of the faculty: correct assent, which is virtue. Calm follows from it — lines 17, 19 and 23 give the Stoic three kinds of positive feeling — but it follows. It is a consequence of judging truly, never the target aimed at.

This is the distinction between the condition of the faculty and how it feels, collapsed at the level of the whole program rather than in a single exercise. The same collapse was found in the Lesson 2 HSA run and again in the answer to Lesson 3. The Introduction shows it is not a lapse in particular lessons. It is the stated design.


VI. Sterling Answered This Proposal in 2008

Robertson writes that the techniques matter because they often anticipate modern evidence-based psychological techniques, and that technique one is a core CBT method originally inspired by the Stoics.

In February 2008 Jules Evans put that case to Grant Sterling on the International Stoic Forum. Evans argued that the core insight is easier to accept and more practically useful than the radical claim that virtue is the only source of happiness, and that CBT and positive psychology had successfully taken up the Stoic techniques without the full doctrine. Sterling’s reply runs on three fronts.

First, the concession is not available. On his argument, holding that suffering comes from our own beliefs rather than from externals just is holding that externals are neither good nor evil — because a genuine evil perceived would cause suffering. The “easier” insight already contains the “radical” one (Stoicism Is Not Therapy, But Training, Part Three).

Second, a technique has no content apart from the doctrine that supplies its object. Sterling’s question: train oneself to stay mindful of what? Keep thought journals about what? Each technique either reflects the doctrine — we challenge negative thoughts because those thoughts rest on the false belief that externals have value — or is not distinctively Stoic at all, since many systems recommend mindfulness of something.

Third, the clinical evidence runs the other way. Sterling: whatever success CBT has had is the result of incorporating Stoic doctrine, and he sees no reason to think it would have less success if it incorporated more.

He also made the concession that should be recorded here. He has no objection whatsoever to the expansion of Stoic-based psychological programs, and someone adopting the first and most basic Stoic principles and working upward is making progress. The finding against the Introduction is not that the five techniques are worthless. It is that what makes any of them Stoic is the doctrine, and the doctrine is scheduled fourth.

The earlier part of the same exchange settles the related point about recovery: Stoic medicine works as immunization, and where it cures it cures by changing beliefs (Parts One and Two).


VII. The Promontory

Robertson quotes Meditations 4.49 and then glosses the accompanying image: the figure looks down at the storm not because he has escaped it but because his vantage point lets him calmly take it in.

Marcus’s promontory has no vantage point. It has a nature — it stands, and the water tires around it. And the chapter does not stop at the image: Marcus goes on to say that what should be applied on such occasions is not that the thing is a misfortune, but that bearing it nobly is good fortune. That is a value claim about the event and about the act of bearing it.

The gloss replaces the value claim with a point of view. Nothing is said about what the storm is worth. The reader is given a position from which to watch it.


VIII. What a Five-Part Program Looks Like When It Is Derived

The corpus already has one, and it is not a collection of techniques. It is Excerpt 7’s six clauses, in Sterling’s lettering:

  • (a) Do not assent to impressions that assign good or evil to externals.
  • (b) If (a) fails, do not then assent to impressions depicting an immoral response as appropriate.
  • (c) Consciously formulate true propositions about the lack of value of external things — in advance where possible, and at the time regardless.
  • (d) Consciously formulate true action propositions, bring them to mind, and assent to them: correct rational ends, rational means, with the reservation that outcomes are never under one’s control (Excerpt 10).
  • (e) Assent to what is already possessed.
  • (f) The terminus.

Each clause is tied to the chain above it. (a) runs on Th10 and 12. (b) runs on Th27. (c) is the advance work that makes recovery fast. (d) runs on Th24, Th25 and Th29. Remove Th10 and every clause loses its object. That is what a derived program looks like, and why the order cannot be rearranged for ease of teaching. The clauses are developed at length in The Day Organized Around Action and set out for builders in The Application Builder’s Kit.


IX. Finding

The Introduction announces a system and delivers a sequence. The five techniques are ordered by accessibility, not derived from one another, and the proposition that would give any of them an object is placed fourth and then stated as a source of confidence rather than as the truth about where value lies. The end served throughout is a feeling-state.

Sterling’s answer to this exact proposal is eighteen years old and has not been met: the technique carries nothing Stoic apart from the doctrine, and the doctrine, taught last, cannot ground what came before it.

Coverage. This answer rests on the free introductory post of 28 July 2026 only. The infographic PDF and the welcome audio were not opened. Lessons 1, 4, 5 and 6 were not opened; the only other Robertson material entering is the cross-reference to the Lesson 2 HSA run and to the Lesson 3 answer in §V.


Theoretical foundations: Grant C. Sterling (Eastern Illinois University). Analysis and synthesis: Dave Kelly. Prose rendering: Claude (Anthropic). 2026.

Thursday, October 01, 2026

The Classical Deliberation Validator and Derivation Trace Instrument — Reference Suite v0.5.4

 

The Classical Deliberation Validator and Derivation Trace Instrument — Reference Suite v0.5.4

Theoretical foundations: Grant C. Sterling (Eastern Illinois University). Analysis and synthesis: Dave Kelly. Prose rendering: Claude (Anthropic). 2026.

Architecture and protocol design: Dave Kelly. Code: v0.5.2 written by Gemini (Google); v0.5.3 and v0.5.4 by Claude (Anthropic). Release date: October 1, 2026. Dedicated to the public domain under CC0 1.0 Universal.


The Beginning

Everyone wants happiness. Incomplete happiness is not worth accepting if complete happiness is available. Complete happiness must therefore lie wholly within our control. Only assent lies wholly within our control. Happiness is therefore correct assent, correct when it matches the truth about value. The truth about value is that only virtue is good and only vice is evil. Everything else is indifferent. (Core Stoicism.)


Purpose

The instrument is for the individual, not the corporation. It is built for the corporate sphere because that is where an individual’s assent is most tested: under steady pressure to treat corporate externals (targets, approval, security) as goods, and to accept “appropriate” responses that are really compliance with that false valuation.

It is a defensive instrument. People in corporate life are told to document everything. This is that advice done properly. It does two things:

  • It assists the decision. The person writes down what he was asked to do, the facts, and his own judgments of what is at stake. The instrument checks those judgments against Sterling’s system and shows him, with the proposition quoted, where one is false.
  • It keeps a record. The record holds the request, the facts, his judgments, the findings and his decision, all dated, made at the time.

The individual must be the one to assent. The machine never does. What the machine can do rests on a fact about Sterling’s system: it is rigorous and consistent enough to be run deterministically. The same case always gives the same findings. No one can argue with the instrument, persuade it or lean on it.


How It Works

  • 1. He fills out a case form. The form has nine sections: the case, the request, the facts, what is at stake, his roles, any conflicts between them, his aims, the actions he is weighing, and his decision. A companion manual, Filling Out the Case Form, explains each one.
  • 2. The instrument checks the form. If the form breaks a rule, the run halts and names the line. Nothing is judged until the form is well formed.
  • 3. The instrument runs the gates and prints a record. Every finding quotes the proposition it rests on.
  • 4. He decides. He enters his decision in the form, and the record keeps it in his own words. If he assents to an action that failed the gates, the record says so. It does not override him.

The Gates

  • False value judgments. An external marked good or evil is false (SLE Props 18 and 20). Something in his own choice marked indifferent is also false, because good and evil lie there (SLE Props 16 and 17). An action that rests on a false judgment fails.
  • Aims. An aim must be a preferred indifferent, held as an object of aim and not as a good (SLE Props 22 and 60).
  • Reservation. Every action must be held with reservation: if it can be done (SLE Prop 62).
  • Vice. An action that involves a vice fails, whatever any role or instruction says (SLE Props 17 and 61). The list is closed to four: folly, injustice, cowardice and intemperance.
  • Facts. An action that depends on a fact not yet verified is marked pending until the fact is checked (The Occasion Router, Section VI).
  • Roles. A role comes from an actual relationship, not from a title or a charter (SLE Prop 65). No document has to grant it.

There is no gate for where an action lies. An action is a choice, never an external deed (SLE Prop 32; Sterling, Excerpt 10), so the form has no field in which to say otherwise.


Conflicts Between Roles

Where two roles pull apart, the person answers six questions, and the instrument applies them in order, stopping at the first that decides. This is the Role-Precedence Decision Tree of the Sterling Logic Engine (Expansion 1; SLE Props 69 and 70).

  • 1. Which role’s removal would end the conflict?
  • 2. Would delay be irreversible for one duty and not the other?
  • 3. Can one duty be done first and the other resumed?
  • 4. Is one role held only because of the other?
  • 5. Which failure would be the greater wrong in his own will?
  • 6. Whose people depend on him more?

The role that yields is not abandoned. Its duties come after, as far as they can be honored. If no question decides, the roles are of equal precedence, and both are honored as fully as they allow together.


The Record

Each action comes back with one outcome:

  • FAILS: it rests on a false value judgment, aims at something held as a good, lacks reservation, or involves a vice.
  • PENDING: it passes those gates but depends on an unverified fact.
  • SUBORDINATE: it passes, but its role yields in a conflict.
  • EQUAL_PRECEDENCE: it passes, and its role ties in a conflict.
  • ASSENT_ELIGIBLE: it passes every gate. Whether to assent is his alone (SLE Prop 11).

The record carries the SHA-256 hash of the form it was made from, so a record can be matched to its form.


The Verifier

The Derivation Trace Instrument checks a certificate after the kernel has issued it:

  • it re-derives every action’s outcome by a second, separate implementation;
  • it checks every quoted proposition against the corpus texts held in the file;
  • it compares the findings with a fresh evaluation of the case;
  • given the form, it checks the form’s hash and that the case data match the form.

Declared Limits

  • L1. The case as classified. The instrument checks the case as the person classified it. If he marks “keeping my job is a moral good” as preferred, it is checked as preferred. Classifying is his assent.
  • L2. Reservation. Reservation is recorded as true or false. The instrument cannot tell reservation actually held from reservation held in name only (SLE Prop 79).
  • L3. Not a verdict. A record states what the gates found in the case as written. It is not a verdict that any act is right.
  • L4. Whole roles. A conflict is settled between whole roles. Every action under the yielding role is marked subordinate, even one that does not collide with the governing role’s duty. The battery demonstrates this.
  • L5. One assent. The decision records assent to one action. A second assent that does not conflict with the first goes in the decision’s note.

L1 and L2 are the D2 failure mode: the right words with no act of assent behind them. No instrument can close them. L4 and L5 are limits of this version, to be removed in the next.


Conformance

Running the file with no arguments runs the whole battery:

  • Three cases whose outcomes were worked by hand before the code was written: a trust officer told to move a trust’s holdings into his bank’s own higher-fee funds; the same case with the key fact unchecked; and the same case where he complies anyway.
  • Determinism: the same form gives the same certificate and the same record.
  • Ten malformed forms, each of which must halt with the fault named.
  • Eleven mutants: a valid certificate is corrupted in eleven ways, and the verifier must reject each with the expected flag.
  • One declared-limit demonstration (L4), printed as such.

A complete run ends with this line:

CONFORMANCE: ALL CASES, FORMS AND MUTANTS DISCRIMINATED AS EXPECTED

Running It

The suite is one Python file. It uses only the standard library and runs on Python 3.7 or later. Save the Source section below as cdv_suite_v0_5_4.py.

  • To run the battery: python3 cdv_suite_v0_5_4.py
  • To get a blank case form: python3 cdv_suite_v0_5_4.py --template
  • To check a case: python3 cdv_suite_v0_5_4.py my_case.txt

In an AI chat, code execution must be switched on. Attach the saved file and your case form and ask: “Run cdv_suite_v0_5_4.py on my_case.txt with Python and show me the output.” Or give the AI this page and ask it to “extract the Source section, save it as a Python file, and run it.” A real run shows a code-execution step, and its output comes from the tool. A reply without that step is a description of what the output would be, not a run.


Changes from v0.5.3

  • A new case form, rebuilt from the corpus. It adds the request, what is at stake, conflicts between roles, the date and the decision.
  • Roles come from actual relationships. v0.5.3 kept v0.5.2’s premise that a role exists only if a document grants it. That premise is dropped, with its evidence tiers.
  • Role conflicts are resolved by the Role-Precedence Decision Tree. v0.5.3 had nothing for a conflict between roles.
  • Findings quote their propositions. The instrument attaches the corpus text to each finding, so the user never has to name an anchor.
  • Vices are a closed list of four. Execution modes are dropped; they did not come from the corpus.
  • A record is produced, with the person’s decision.
  • Malformed forms halt with the line and the fault named.

Integrity

The SHA-256 hash of the release file, cdv_suite_v0_5_4.py, is:

48156ca2d9373b5a49a158f085857f2612fcb16f7b728098b10c2e0d36b52840  cdv_suite_v0_5_4.py

The hash is computed on the file itself. Text copied from this page may differ in line endings or spacing, and its hash will then differ. To check a copy, run sha256sum cdv_suite_v0_5_4.py and compare.


Source

The complete source of v0.5.4 follows.

"""
CDV/DTI REFERENCE SUITE -- BUILD v0.5.4
=======================================
Classical Deliberation Validator (CDV) & Derivation Trace Instrument (DTI)

Theoretical foundations: Grant C. Sterling (Eastern Illinois University).
Architecture and engineering: Dave Kelly.
Code: v0.5.2 written by Gemini (Google); v0.5.3 and v0.5.4 by Claude
(Anthropic).
Release date: 1 October 2026.
License: CC0 1.0 Universal (dedicated to the public domain).

THE BEGINNING
-------------
Everyone wants happiness. Incomplete happiness is not worth accepting if
complete happiness is available. Complete happiness must therefore lie
wholly within our control. Only assent lies wholly within our control.
Happiness is therefore correct assent, correct when it matches the truth
about value: only virtue is good, only vice is evil, everything else is
indifferent.

PURPOSE
-------
For the individual, not the corporation. A person facing pressure at work
to treat targets, approval or security as goods, and to accept an
"appropriate" response that is really compliance, fills out a case form.
The instrument checks the judgment he has written down against Sterling's
system, deterministically, and produces a record of the facts, the
findings and his decision. He alone assents. The instrument never decides.

HOW IT RUNS
-----------
  python3 cdv_suite_v0_5_4.py                 run the conformance battery
  python3 cdv_suite_v0_5_4.py my_case.txt     check a case form, print record
  python3 cdv_suite_v0_5_4.py --template      print a blank case form

AIRTIGHT RULES FOR THE FORM
---------------------------
  1. A [READ] field takes one value from its closed list, or the run halts.
  2. A [RECORD] field is a quoted string. It is kept, never judged.
  3. A missing required field halts the run.
  4. Every ID cited must exist in the form, with the right prefix.
  5. The same form always gives the same record, byte for byte.
  6. Every finding cites the proposition it rests on.

DECLARED LIMITS
---------------
L1. The instrument checks the case as the person classified it. If he
    marks "keeping my job is a moral good" as PREFERRED, it is checked as
    PREFERRED. Classifying is his assent.
L2. Reservation is recorded TRUE or FALSE. The instrument cannot tell
    reservation held from reservation held in name only (SLE Prop 79).
L3. A record is not a verdict that the act is right. It states what the
    gates found in the case as written. His assent is his.
L4. A conflict is settled between whole roles. Every action under the
    yielding role is marked SUBORDINATE, even one that does not collide
    with the governing role's duty. (Demonstrated in the battery.)
L5. The decision records assent to one action. A second assent that does
    not conflict with the first goes in the decision's note.

CHANGES FROM v0.5.3
-------------------
  * New case form, rebuilt from the corpus (draft 2, approved 30 Sep 2026):
    request, facts, what is at stake, roles from actual relationships
    (Prop 65), conflicts between roles, aims, actions, and the decision.
  * Roles no longer require a granting document; evidence tiers dropped.
  * Execution modes dropped (not from the corpus).
  * Vices closed to FOLLY, INJUSTICE, COWARDICE, INTEMPERANCE.
  * Role conflicts resolved by the Role-Precedence Decision Tree
    (SLE Expansion 1, criteria in order; Props 69-70).
  * Findings cite SLE propositions, quoted, attached by the instrument.
  * A record of facts, findings and decision is produced.
"""

from __future__ import annotations

import copy
import datetime
import hashlib
import json
import re
import sys
from typing import Any, Dict, List, Optional, Tuple

VERSION = "0.5.4"

# ============================================================================
# 1. CORPUS: propositions cited by the findings
# ============================================================================
# Quoted from the Sterling Logic Engine v4.4, Part 4 (mirror of v4.0), with
# ASCII punctuation. Props 1-58: Grant C. Sterling, synthesized by Dave
# Kelly. Props 59-80 (Action Proposition Set): Dave Kelly, on Sterling's
# theoretical foundations. ROUTER_VI: The Occasion Router v1.3, Section VI.

CORPUS: Dict[str, str] = {
    "PROP_11": "The act of assenting to (or rejecting) impressions is the only "
               "thing in our control.",
    "PROP_16": "Only things directly related to virtue (beliefs, desires, "
               "will/choice) are in our control.",
    "PROP_17": "Only virtue is genuinely good; only vice is genuinely evil.",
    "PROP_18": "All things not in our control (externals) are neither genuinely "
               "good nor genuinely evil.",
    "PROP_20": "The belief that any external is good or evil is factually false.",
    "PROP_22": "Preferred indifferents are appropriate objects to aim at, though "
               "not genuinely good.",
    "PROP_29": "Therefore, all desires for externals are based on false beliefs.",
    "PROP_60": "A rational goal is a preferred indifferent held as an appropriate "
               "object of aim. It is not a desired outcome held as a genuine good.",
    "PROP_61": "Rational means are those genuinely designed to realize the "
               "rational goal, that are not themselves immoral, and that are "
               "proportionate to the full range of the agent's rational goals at "
               "that moment.",
    "PROP_62": "Reservation is the constitutive framing of every rational act of "
               "will. The agent aims at the goal if the control dichotomy allows "
               "-- not unconditionally.",
    "PROP_65": "Roles are identified by the actual social relationships the agent "
               "stands in, not by the relationships he desires, believes he ought "
               "to have, or would prefer.",
    "PROP_69": "The determination rule is: all other things being equal, maximize "
               "preferred indifferents across all roles simultaneously.",
    "PROP_70": "When roles conflict, the agent identifies which role is most "
               "directly operative in this situation and discharges its duties "
               "first, without abandoning the duties of the other roles entirely.",
    "ROUTER_VI": "When an occasion carries an unverified fact, Route Two runs first.",
}

CORPUS_LABELS: Dict[str, str] = {
    k: ("SLE Prop " + k[5:] if k.startswith("PROP_") else "The Occasion Router, Section VI")
    for k in CORPUS
}

# Each finding: plain statement, and the corpus passages it rests on.
FINDINGS: Dict[str, Tuple[str, List[str]]] = {
    "VALUE_FALSE_EXTERNAL": (
        "{id} is marked {pol} and EXTERNAL. An external is neither good nor evil, "
        "so this judgment is false.", ["PROP_18", "PROP_20"]),
    "VALUE_FALSE_INTERNAL": (
        "{id} is marked {pol} and MY_CHOICE. What lies in your own choice is where "
        "good and evil are found; it is not indifferent.", ["PROP_16", "PROP_17"]),
    "CONTAMINATED_EXTERNAL": (
        "This action rests on {id}, a false value judgment. An action that rests on "
        "treating an external as good or evil rests on a false belief.",
        ["PROP_20", "PROP_29"]),
    "CONTAMINATED_INTERNAL": (
        "This action rests on {id}, a false value judgment. It treats what lies in "
        "your own choice as indifferent, when good and evil lie there.",
        ["PROP_16", "PROP_17"]),
    "AIM_INVALID": (
        "This action aims at {id}, marked {pol}. An aim must be a preferred "
        "indifferent, held as an object of aim, not as a good.", ["PROP_22", "PROP_60"]),
    "RESERVATION_ABSENT": (
        "This action is not held with reservation, 'if it can be done'.", ["PROP_62"]),
    "VICE": (
        "This action involves {vices}. A means that is itself a vice is excluded, "
        "whatever any role or instruction says.", ["PROP_17", "PROP_61"]),
    "FACT_UNVERIFIED": (
        "This action depends on {id}, which is {status}. Check the fact before "
        "acting on it.", ["ROUTER_VI"]),
    "SUBORDINATE": (
        "This action belongs to {role}, which yields to {winner} in {conflict} "
        "(decided by {criterion}). Discharge {winner}'s duty first, without "
        "abandoning this one.", ["PROP_70"]),
    "EQUAL_PRECEDENCE": (
        "{role} and {other} are of equal precedence in {conflict}. Honor the duties "
        "of both as fully as they allow together.", ["PROP_69"]),
    "ASSENT_ELIGIBLE": (
        "This action passes every gate. It is eligible for your assent. Whether to "
        "give it is yours alone.", ["PROP_11"]),
}

CRITERIA = [
    "Criterion 1, direct causation",
    "Criterion 2, irreversible delay",
    "Criterion 3, discharge one and resume the other",
    "Criterion 4, dependence of one role on the other",
    "Criterion 5, the greater wrong in your own will",
    "Criterion 6, the people more dependent on you",
]

# ============================================================================
# 2. THE FORM: closed vocabularies and schema
# ============================================================================

POLARITY = {"GOOD", "EVIL", "PREFERRED", "DISPREFERRED", "INDIFFERENT"}
LOCUS = {"MY_CHOICE", "EXTERNAL"}
FACT_STATUS = {"VERIFIED", "UNVERIFIED", "DISPUTED"}
VICES = {"FOLLY", "INJUSTICE", "COWARDICE", "INTEMPERANCE"}
YES_NO = {"YES", "NO"}
FSBN = {"FIRST", "SECOND", "BOTH", "NEITHER"}
FSE = {"FIRST", "SECOND", "EQUAL"}

SECTIONS = ["CASE", "REQUEST", "FACTS", "AT_STAKE", "ROLES", "CONFLICTS",
            "AIMS", "ACTIONS", "DECISION"]
PREFIX = {"REQUEST": "REQ_", "FACTS": "F_", "AT_STAKE": "V_", "ROLES": "R_",
          "CONFLICTS": "C_", "AIMS": "A_", "ACTIONS": "ACT_"}
HEADER_TOKENS = {"FACTS": 1, "AT_STAKE": 2, "AIMS": 1}

# Field kinds: "record" quoted string; "date"; "token:<SET>"; "ref:<SECTION>";
# "refs:<SECTIONS>"; "pressed"; "assent"; "vices"; "rolepair".
FIELDS: Dict[str, Dict[str, str]] = {
    "CASE": {"PREPARED_BY": "record", "DATE": "date", "SITUATION": "record"},
    "REQUEST": {"DATE": "date", "FROM": "record", "RELATIONSHIP": "record",
                "WITNESSES": "record", "WORDS": "record", "REASON_GIVEN": "record"},
    "FACTS": {"TEXT": "record", "HOW_VERIFIED": "record"},
    "AT_STAKE": {"TEXT": "record"},
    "ROLES": {"NAME": "record", "RELATIONSHIP": "record", "DUTY": "record",
              "MANNER": "record"},
    "CONFLICTS": {"ROLES": "rolepair",
                  "EXISTS_WITHOUT_FIRST": "token:YES_NO",
                  "EXISTS_WITHOUT_SECOND": "token:YES_NO",
                  "DELAY_IRREVERSIBLE": "token:FSBN",
                  "CAN_DO_FIRST_THEN_RESUME": "token:FSBN",
                  "HELD_BECAUSE_OF_OTHER": "token:FSBN",
                  "GREATER_WRONG_IF_FAILED": "token:FSE",
                  "MORE_DEPENDENT_PEOPLE": "token:FSE"},
    "AIMS": {"TEXT": "record"},
    "ACTIONS": {"PRESSED_BY": "pressed", "ROLE": "ref:ROLES", "AIM": "ref:AIMS",
                "RESERVATION": "token:BOOL", "DEPENDS_ON": "refs:FACTS,AT_STAKE",
                "VICES": "vices", "DESC": "record", "MEANS": "record"},
    "DECISION": {"DATE": "date", "ASSENT": "assent", "NOTE": "record"},
}
TOKEN_SETS = {"YES_NO": YES_NO, "FSBN": FSBN, "FSE": FSE, "BOOL": {"TRUE", "FALSE"}}


class FormError(ValueError):
    """The form is not well formed. The run halts; nothing is judged."""


_ID = r"[A-Z][A-Z0-9_]*"
_SECTION_LINE = re.compile(r"^(%s)(?:\s+(%s))?:$" % ("|".join(SECTIONS), _ID))
_ITEM_LINE = re.compile(r"^(%s):(?:\s*\[([^\]]*)\])?$" % _ID)
_FIELD_LINE = re.compile(r"^([A-Z_]+):\s*(.*)$")
_QUOTED = re.compile(r'^"(.*)"$')


def _tokens(raw: str) -> List[str]:
    raw = raw.strip()
    if not (raw.startswith("[") and raw.endswith("]")):
        raise FormError(f"Expected a bracketed list, got: {raw}")
    inner = raw[1:-1].strip()
    return [t.strip() for t in inner.split(",")] if inner else []


def parse_form(text: str) -> Dict[str, Any]:
    case: Dict[str, Any] = {s: {} for s in SECTIONS}
    case["CASE_ID"] = None
    seen_sections: List[str] = []
    all_ids: Dict[str, str] = {}
    section: Optional[str] = None
    item: Optional[Dict[str, Any]] = None

    for n, raw in enumerate(text.splitlines(), 1):
        line = raw.strip()
        if not line or line.startswith("#"):
            continue
        where = f"line {n}"
        m = _SECTION_LINE.match(line)
        if m:
            section = m.group(1)
            if section in seen_sections:
                raise FormError(f"{where}: section {section} appears twice")
            seen_sections.append(section)
            item = None
            if section == "CASE":
                if not m.group(2):
                    raise FormError(f"{where}: CASE needs an identifier")
                case["CASE_ID"] = m.group(2)
            elif m.group(2):
                raise FormError(f"{where}: only CASE takes an identifier")
            continue
        if section is None:
            raise FormError(f"{where}: line outside any section: {line}")

        if section in PREFIX:
            im = _ITEM_LINE.match(line)
            if im and im.group(1) not in FIELDS[section]:
                item_id, bracket = im.group(1), im.group(2)
                if not item_id.startswith(PREFIX[section]):
                    raise FormError(f"{where}: {item_id} must begin with "
                                    f"{PREFIX[section]} in {section}")
                if item_id in all_ids:
                    raise FormError(f"{where}: duplicate ID {item_id}")
                all_ids[item_id] = section
                item = {"_fields": {}}
                want = HEADER_TOKENS.get(section, 0)
                toks = [t.strip() for t in bracket.split(",")] if bracket else []
                if bracket is not None and bracket.strip() == "":
                    toks = []
                if len(toks) != want:
                    raise FormError(f"{where}: {item_id} needs {want} bracketed "
                                    f"value(s), got {len(toks)}")
                if section == "FACTS":
                    _check_token(toks[0], FACT_STATUS, where, "fact status")
                    item["STATUS"] = toks[0]
                elif section == "AT_STAKE":
                    _check_token(toks[0], POLARITY, where, "polarity")
                    _check_token(toks[1], LOCUS, where, "locus")
                    item["POLARITY"], item["LOCUS"] = toks
                elif section == "AIMS":
                    _check_token(toks[0], POLARITY, where, "polarity")
                    item["POLARITY"] = toks[0]
                case[section][item_id] = item
                continue

        fm = _FIELD_LINE.match(line)
        if not fm:
            raise FormError(f"{where}: cannot read: {line}")
        key, value = fm.group(1), fm.group(2).strip()
        if key not in FIELDS[section]:
            raise FormError(f"{where}: {key} is not a field of {section}")
        target = case[section] if section in ("CASE", "DECISION") else item
        if target is None:
            raise FormError(f"{where}: field {key} before any item in {section}")
        fields = target["_fields"] if "_fields" in target else target
        if key in fields:
            raise FormError(f"{where}: {key} given twice")
        fields[key] = _read_value(FIELDS[section][key], value, where, key)

    _check_complete(case, all_ids)
    return case


def _check_token(tok: str, allowed: set, where: str, what: str) -> None:
    if tok not in allowed:
        raise FormError(f"{where}: {tok!r} is not a valid {what}; "
                        f"use one of {', '.join(sorted(allowed))}")


def _read_value(kind: str, value: str, where: str, key: str) -> Any:
    if kind == "record":
        q = _QUOTED.match(value)
        if not q:
            raise FormError(f"{where}: {key} must be a quoted string")
        return q.group(1)
    if kind == "date":
        try:
            datetime.date.fromisoformat(value)
        except ValueError:
            raise FormError(f"{where}: {key} must be a date YYYY-MM-DD, got {value!r}")
        return value
    if kind.startswith("token:"):
        _check_token(value, TOKEN_SETS[kind[6:]], where, key)
        return value
    if kind == "vices":
        toks = _tokens(value)
        for t in toks:
            _check_token(t, VICES, where, "vice")
        if len(set(toks)) != len(toks):
            raise FormError(f"{where}: a vice is listed twice")
        return sorted(toks)
    if kind == "rolepair":
        toks = [t.strip() for t in value.split(",")]
        if len(toks) != 2 or toks[0] == toks[1]:
            raise FormError(f"{where}: ROLES must name two different roles")
        return toks
    if kind.startswith("refs:"):
        toks = _tokens(value)
        if len(set(toks)) != len(toks):
            raise FormError(f"{where}: {key} lists an ID twice")
        return toks
    # ref:, pressed, assent: single identifier or keyword, resolved later
    if not re.fullmatch(_ID, value):
        raise FormError(f"{where}: {key} must be a single identifier, got {value!r}")
    return value


def _check_complete(case: Dict[str, Any], all_ids: Dict[str, str]) -> None:
    if case["CASE_ID"] is None:
        raise FormError("The form has no CASE section")
    for k in FIELDS["CASE"]:
        if k not in case["CASE"]:
            raise FormError(f"CASE is missing {k}")
    if not case["ROLES"]:
        raise FormError("The form names no roles")
    if not case["ACTIONS"]:
        raise FormError("The form names no actions")
    for section in PREFIX:
        for item_id, item in case[section].items():
            for k in FIELDS[section]:
                if k not in item["_fields"]:
                    raise FormError(f"{item_id} is missing {k}")
    if case["DECISION"]:
        for k in FIELDS["DECISION"]:
            if k not in case["DECISION"]:
                raise FormError(f"DECISION is missing {k}")

    def resolve(ref: str, sections: List[str], who: str) -> None:
        if all_ids.get(ref) not in sections:
            raise FormError(f"{who} cites {ref}, which is not in "
                            f"{' or '.join(sections)}")

    pairs = set()
    for cid, c in case["CONFLICTS"].items():
        a, b = c["_fields"]["ROLES"]
        resolve(a, ["ROLES"], cid)
        resolve(b, ["ROLES"], cid)
        if frozenset((a, b)) in pairs:
            raise FormError(f"{cid}: the conflict between {a} and {b} is entered twice")
        pairs.add(frozenset((a, b)))
    for aid, a in case["ACTIONS"].items():
        f = a["_fields"]
        resolve(f["ROLE"], ["ROLES"], aid)
        resolve(f["AIM"], ["AIMS"], aid)
        if f["PRESSED_BY"] != "NONE":
            resolve(f["PRESSED_BY"], ["REQUEST"], aid)
        for d in f["DEPENDS_ON"]:
            resolve(d, ["FACTS", "AT_STAKE"], aid)
    if case["DECISION"]:
        assent = case["DECISION"]["ASSENT"]
        if assent != "WITHHELD":
            resolve(assent, ["ACTIONS"], "DECISION")


# ============================================================================
# 3. KERNEL: gates, precedence, findings
# ============================================================================

def finding(code: str, **kw: Any) -> Dict[str, Any]:
    text, cites = FINDINGS[code]
    return {"code": code, "text": text.format(**kw),
            "cites": [{"ref": c, "label": CORPUS_LABELS[c], "text": CORPUS[c]}
                      for c in cites]}


def resolve_conflict(c: Dict[str, str]) -> Tuple[str, int]:
    """Role-Precedence Decision Tree (SLE Expansion 1): criteria in order, the
    first decisive one ends it. Returns (FIRST|SECOND|EQUAL, criterion 1-6|0)."""
    wf, ws = c["EXISTS_WITHOUT_FIRST"], c["EXISTS_WITHOUT_SECOND"]
    if wf == "NO" and ws == "YES":
        return "FIRST", 1
    if wf == "YES" and ws == "NO":
        return "SECOND", 1
    if c["DELAY_IRREVERSIBLE"] in ("FIRST", "SECOND"):
        return c["DELAY_IRREVERSIBLE"], 2
    if c["CAN_DO_FIRST_THEN_RESUME"] in ("FIRST", "SECOND"):
        return c["CAN_DO_FIRST_THEN_RESUME"], 3
    held = c["HELD_BECAUSE_OF_OTHER"]  # the role held because of the other
    if held == "SECOND":
        return "FIRST", 4
    if held == "FIRST":
        return "SECOND", 4
    if c["GREATER_WRONG_IF_FAILED"] in ("FIRST", "SECOND"):
        return c["GREATER_WRONG_IF_FAILED"], 5
    if c["MORE_DEPENDENT_PEOPLE"] in ("FIRST", "SECOND"):
        return c["MORE_DEPENDENT_PEOPLE"], 6
    return "EQUAL", 0


def evaluate(case: Dict[str, Any]) -> Dict[str, Any]:
    case_findings: List[Dict[str, Any]] = []
    false_values: Dict[str, str] = {}
    for vid, v in case["AT_STAKE"].items():
        pol, loc = v["POLARITY"], v["LOCUS"]
        if loc == "EXTERNAL" and pol in ("GOOD", "EVIL"):
            false_values[vid] = "CONTAMINATED_EXTERNAL"
            case_findings.append({"on": vid, **finding("VALUE_FALSE_EXTERNAL", id=vid, pol=pol)})
        elif loc == "MY_CHOICE" and pol not in ("GOOD", "EVIL"):
            false_values[vid] = "CONTAMINATED_INTERNAL"
            case_findings.append({"on": vid, **finding("VALUE_FALSE_INTERNAL", id=vid, pol=pol)})

    conflicts: Dict[str, Dict[str, Any]] = {}
    for cid, c in case["CONFLICTS"].items():
        f = c["_fields"]
        first, second = f["ROLES"]
        result, crit = resolve_conflict(f)
        conflicts[cid] = {
            "roles": [first, second], "result": result, "criterion": crit,
            "operative": first if result == "FIRST" else second if result == "SECOND" else None,
            "criterion_name": CRITERIA[crit - 1] if crit else "no criterion decisive",
        }

    actions: Dict[str, Dict[str, Any]] = {}
    for aid, a in case["ACTIONS"].items():
        f = a["_fields"]
        fails, pending, standing = [], [], []
        for dep in f["DEPENDS_ON"]:
            if dep in false_values:
                fails.append(finding(false_values[dep], id=dep))
        aim = case["AIMS"][f["AIM"]]
        if aim["POLARITY"] != "PREFERRED":
            fails.append(finding("AIM_INVALID", id=f["AIM"], pol=aim["POLARITY"]))
        if f["RESERVATION"] != "TRUE":
            fails.append(finding("RESERVATION_ABSENT"))
        if f["VICES"]:
            fails.append(finding("VICE", vices=", ".join(f["VICES"])))
        for dep in f["DEPENDS_ON"]:
            if dep in case["FACTS"] and case["FACTS"][dep]["STATUS"] != "VERIFIED":
                pending.append(finding("FACT_UNVERIFIED", id=dep,
                                       status=case["FACTS"][dep]["STATUS"]))
        for cid, r in conflicts.items():
            if f["ROLE"] not in r["roles"]:
                continue
            other = r["roles"][1] if r["roles"][0] == f["ROLE"] else r["roles"][0]
            if r["result"] == "EQUAL":
                standing.append(finding("EQUAL_PRECEDENCE", role=f["ROLE"], other=other,
                                        conflict=cid))
            elif r["operative"] != f["ROLE"]:
                standing.append(finding("SUBORDINATE", role=f["ROLE"], winner=r["operative"],
                                        conflict=cid, criterion=r["criterion_name"]))
        if fails:
            outcome = "FAILS"
        elif pending:
            outcome = "PENDING"
        elif any(s["code"] == "SUBORDINATE" for s in standing):
            outcome = "SUBORDINATE"
        elif standing:
            outcome = "EQUAL_PRECEDENCE"
        else:
            outcome = "ASSENT_ELIGIBLE"
        found = fails + pending + standing
        if outcome == "ASSENT_ELIGIBLE":
            found = [finding("ASSENT_ELIGIBLE")]
        actions[aid] = {"outcome": outcome, "pressed_by": f["PRESSED_BY"],
                        "findings": found}

    decision = None
    if case["DECISION"]:
        d = case["DECISION"]
        assent = d["ASSENT"]
        note = None
        if assent != "WITHHELD" and actions[assent]["outcome"] != "ASSENT_ELIGIBLE":
            note = (f"You assented to {assent}, whose outcome is "
                    f"{actions[assent]['outcome']}. The instrument records this; "
                    f"it does not override your assent.")
        decision = {"date": d["DATE"], "assent": assent, "note": d["NOTE"],
                    "instrument_note": note}

    return {"case_findings": case_findings, "conflicts": conflicts,
            "actions": actions, "decision": decision}


def certify(form_text: str) -> Dict[str, Any]:
    case = parse_form(form_text)
    return {
        "version": VERSION,
        "form_sha256": hashlib.sha256(form_text.encode("utf-8")).hexdigest(),
        "case_id": case["CASE_ID"],
        "case": case,
        "result": evaluate(case),
    }


# ============================================================================
# 4. RECORD: the human-readable record, derived from the certificate only
# ============================================================================

def render_record(cert: Dict[str, Any]) -> str:
    c, r = cert["case"], cert["result"]
    out: List[str] = []
    w = out.append
    rule = "=" * 72
    w(rule)
    w(f"CASE RECORD {cert['case_id']}  (CDV/DTI v{cert['version']})")
    w(f"Form SHA-256: {cert['form_sha256']}")
    w(rule)
    w(f"Prepared by: {c['CASE']['PREPARED_BY']}    Date: {c['CASE']['DATE']}")
    w(f"Situation: {c['CASE']['SITUATION']}")

    def cites(fd: Dict[str, Any], indent: str) -> None:
        for ct in fd["cites"]:
            w(f"{indent}  [{ct['label']}] \"{ct['text']}\"")

    if c["REQUEST"]:
        w("\nREQUEST")
        for rid, q in c["REQUEST"].items():
            f = q["_fields"]
            w(f"  {rid}  {f['DATE']}  from {f['FROM']} ({f['RELATIONSHIP']}); "
              f"witnesses: {f['WITNESSES']}")
            w(f"    Words: \"{f['WORDS']}\"")
            w(f"    Reason given: \"{f['REASON_GIVEN']}\"")
    if c["FACTS"]:
        w("\nFACTS")
        for fid, x in c["FACTS"].items():
            w(f"  {fid} [{x['STATUS']}] {x['_fields']['TEXT']}")
            w(f"    How verified: {x['_fields']['HOW_VERIFIED']}")
    if c["AT_STAKE"]:
        w("\nWHAT YOU JUDGED TO BE AT STAKE")
        flagged = {fd["on"]: fd for fd in r["case_findings"]}
        for vid, v in c["AT_STAKE"].items():
            w(f"  {vid} [{v['POLARITY']}, {v['LOCUS']}] {v['_fields']['TEXT']}")
            if vid in flagged:
                w(f"    FINDING: {flagged[vid]['text']}")
                cites(flagged[vid], "    ")
    w("\nROLES")
    for rid, x in c["ROLES"].items():
        f = x["_fields"]
        w(f"  {rid} {f['NAME']} -- {f['RELATIONSHIP']}")
        w(f"    Duty: {f['DUTY']}   Manner: {f['MANNER']}")
    if r["conflicts"]:
        w("\nCONFLICTS BETWEEN ROLES (SLE Expansion 1)")
        for cid, k in r["conflicts"].items():
            a, b = k["roles"]
            if k["operative"]:
                w(f"  {cid} {a} vs {b}: {k['operative']} is operative, by "
                  f"{k['criterion_name']}.")
            else:
                w(f"  {cid} {a} vs {b}: equal precedence; no criterion decisive.")
    w("\nAIMS")
    for aid, x in c["AIMS"].items():
        w(f"  {aid} [{x['POLARITY']}] {x['_fields']['TEXT']}")
    w("\nACTIONS")
    for aid, x in c["ACTIONS"].items():
        f, res = x["_fields"], r["actions"][aid]
        tag = "  (the action you were asked to take)" if f["PRESSED_BY"] != "NONE" else ""
        w(f"  {aid}: {res['outcome']}{tag}")
        w(f"    {f['DESC']}  Means: {f['MEANS']}")
        w(f"    Role {f['ROLE']}, aim {f['AIM']}, reservation {f['RESERVATION']}, "
          f"vices {', '.join(f['VICES']) or 'none'}")
        for fd in res["findings"]:
            w(f"    - {fd['text']}")
            cites(fd, "    ")
    w("\nDECISION")
    d = r["decision"]
    if d is None:
        w("  Not yet recorded. The decision is yours; enter it in the DECISION section.")
    else:
        w(f"  {d['date']}: {'assent withheld' if d['assent'] == 'WITHHELD' else 'assented to ' + d['assent']}")
        w(f"  Note: {d['note']}")
        if d["instrument_note"]:
            w(f"  Instrument: {d['instrument_note']}")
    w("\nThis record states what the gates found in the case as you wrote it.")
    w("It is not a verdict that any act is right. Your assent is yours alone.")
    w(rule)
    return "\n".join(out)


# ============================================================================
# 5. DTI: independent verification of a certificate
# ============================================================================
# Re-derives every outcome from the certificate's case data by a separate
# implementation, checks every cited text against the corpus, and, if the
# form text is supplied, checks the form hash.

def _dti_outcomes(case: Dict[str, Any]) -> Dict[str, str]:
    moral = ("GOOD", "EVIL")
    bad = {vid for vid, v in case["AT_STAKE"].items()
           if (v["LOCUS"] == "EXTERNAL" and v["POLARITY"] in moral)
           or (v["LOCUS"] == "MY_CHOICE" and v["POLARITY"] not in moral)}
    winners: Dict[str, Tuple[str, str, Optional[str]]] = {}
    for cid, c in case["CONFLICTS"].items():
        f = c["_fields"]
        a, b = f["ROLES"]
        decided: Optional[str] = None
        steps = [
            {("NO", "YES"): a, ("YES", "NO"): b}.get(
                (f["EXISTS_WITHOUT_FIRST"], f["EXISTS_WITHOUT_SECOND"])),
            {"FIRST": a, "SECOND": b}.get(f["DELAY_IRREVERSIBLE"]),
            {"FIRST": a, "SECOND": b}.get(f["CAN_DO_FIRST_THEN_RESUME"]),
            {"SECOND": a, "FIRST": b}.get(f["HELD_BECAUSE_OF_OTHER"]),
            {"FIRST": a, "SECOND": b}.get(f["GREATER_WRONG_IF_FAILED"]),
            {"FIRST": a, "SECOND": b}.get(f["MORE_DEPENDENT_PEOPLE"]),
        ]
        decided = next((s for s in steps if s), None)
        winners[cid] = (a, b, decided)
    out: Dict[str, str] = {}
    for aid, x in case["ACTIONS"].items():
        f = x["_fields"]
        aim_ok = case["AIMS"][f["AIM"]]["POLARITY"] == "PREFERRED"
        failed = (any(d in bad for d in f["DEPENDS_ON"]) or not aim_ok
                  or f["RESERVATION"] != "TRUE" or bool(f["VICES"]))
        unverified = any(case["FACTS"][d]["STATUS"] != "VERIFIED"
                         for d in f["DEPENDS_ON"] if d in case["FACTS"])
        involved = [w for w in winners.values() if f["ROLE"] in (w[0], w[1])]
        loses = any(w[2] is not None and w[2] != f["ROLE"] for w in involved)
        equal = any(w[2] is None for w in involved)
        out[aid] = ("FAILS" if failed else "PENDING" if unverified else
                    "SUBORDINATE" if loses else "EQUAL_PRECEDENCE" if equal else
                    "ASSENT_ELIGIBLE")
    return out


def audit(cert_in: Any, form_text: Optional[str] = None) -> Dict[str, str]:
    cert = json.loads(cert_in) if isinstance(cert_in, str) else copy.deepcopy(cert_in)

    def reject(flag: str, detail: str) -> Dict[str, str]:
        return {"verdict": "REJECTED", "flag": flag, "detail": detail}

    if form_text is not None:
        h = hashlib.sha256(form_text.encode("utf-8")).hexdigest()
        if h != cert["form_sha256"]:
            return reject("FORM_HASH_MISMATCH", "The certificate is not of this form")
        if parse_form(form_text) != cert["case"]:
            return reject("CASE_DATA_MISMATCH", "Case data differs from the form")

    res = cert["result"]
    for group in ([res["case_findings"]] +
                  [a["findings"] for a in res["actions"].values()]):
        for fd in group:
            for ct in fd["cites"]:
                if CORPUS.get(ct["ref"]) != ct["text"] or CORPUS_LABELS.get(ct["ref"]) != ct["label"]:
                    return reject("CITATION_ALTERED", f"{ct['ref']} does not match the corpus")

    case = cert["case"]
    if set(res["actions"]) != set(case["ACTIONS"]):
        return reject("ACTIONS_MISMATCH", "Result and case list different actions")
    expected = _dti_outcomes(case)
    for aid, outcome in expected.items():
        if res["actions"][aid]["outcome"] != outcome:
            return reject("OUTCOME_MISMATCH",
                          f"{aid}: certificate says {res['actions'][aid]['outcome']}, "
                          f"replay gives {outcome}")
    try:
        if evaluate(case) != res:
            return reject("FINDINGS_MISMATCH", "Findings differ from a fresh evaluation")
    except (KeyError, TypeError) as e:
        return reject("CASE_DATA_INVALID", repr(e))
    d = res["decision"]
    if d is not None and d["assent"] != "WITHHELD" and d["assent"] not in case["ACTIONS"]:
        return reject("DECISION_INVALID", "Assent names an action not in the case")
    return {"verdict": "VERIFIED", "flag": "REPLAY_MATCHES", "detail": ""}


# ============================================================================
# 6. FORMS: template, the trust case, conformance battery
# ============================================================================

TEMPLATE = '''\
# CASE FORM -- CDV/DTI v0.5.4
# The beginning: only virtue is good, only vice is evil; everything else
# is indifferent. You fill out this form and you alone assent. The
# instrument checks the case as you classify it. It never decides for you.
# [READ] fields take one value from the list shown. [RECORD] fields are a
# quoted string, kept and never judged. Comments must be on their own lines.

CASE CASE_ID:
  PREPARED_BY: "<name>"
  DATE: YYYY-MM-DD
  SITUATION: "<one factual sentence>"

# What you are being pressed to do. Leave the section out if nothing was asked.
REQUEST:
  REQ_01:
    DATE: YYYY-MM-DD
    FROM: "<name>"
    RELATIONSHIP: "<e.g. my manager>"
    WITNESSES: "<names, or none>"
    WORDS: "<exactly what was said or written>"
    REASON_GIVEN: "<the reason offered>"

# F_ID: [VERIFIED | UNVERIFIED | DISPUTED]
FACTS:
  F_01: [VERIFIED]
    TEXT: "<the fact>"
    HOW_VERIFIED: "<source, or how you will check it>"

# V_ID: [GOOD | EVIL | PREFERRED | DISPREFERRED | INDIFFERENT, MY_CHOICE | EXTERNAL]
AT_STAKE:
  V_01: [PREFERRED, EXTERNAL]
    TEXT: "<what you feel is at stake>"

# Roles come from your actual relationships (SLE Prop 65).
ROLES:
  R_01:
    NAME: "<e.g. administrator of the trust>"
    RELATIONSHIP: "<to whom>"
    DUTY: "<what the role requires of you>"
    MANNER: "<e.g. honest, loyal>"

# Only where two roles pull apart. Answers:
#   EXISTS_WITHOUT_FIRST / _SECOND: YES | NO
#     (if you did not hold that role, would this conflict still exist?)
#   DELAY_IRREVERSIBLE: FIRST | SECOND | BOTH | NEITHER
#   CAN_DO_FIRST_THEN_RESUME: FIRST | SECOND | BOTH | NEITHER
#     (whose duty can be done first, and the other resumed after?)
#   HELD_BECAUSE_OF_OTHER: FIRST | SECOND | BOTH | NEITHER
#     (which role do you hold only because you hold the other?)
#   GREATER_WRONG_IF_FAILED: FIRST | SECOND | EQUAL
#   MORE_DEPENDENT_PEOPLE: FIRST | SECOND | EQUAL
CONFLICTS:
  C_01:
    ROLES: R_01, R_02
    EXISTS_WITHOUT_FIRST: NO
    EXISTS_WITHOUT_SECOND: YES
    DELAY_IRREVERSIBLE: NEITHER
    CAN_DO_FIRST_THEN_RESUME: NEITHER
    HELD_BECAUSE_OF_OTHER: NEITHER
    GREATER_WRONG_IF_FAILED: EQUAL
    MORE_DEPENDENT_PEOPLE: EQUAL

# A_ID: [GOOD | EVIL | PREFERRED | DISPREFERRED | INDIFFERENT]
AIMS:
  A_01: [PREFERRED]
    TEXT: "<what the action aims at>"

# Every option, including the one pressed on you.
# PRESSED_BY: the ID of a request from the REQUEST section, such as REQ_01,
#   or NONE. Never a name.
# VICES: [] or any of FOLLY, INJUSTICE, COWARDICE, INTEMPERANCE
ACTIONS:
  ACT_01:
    PRESSED_BY: NONE
    ROLE: R_01
    AIM: A_01
    RESERVATION: TRUE
    DEPENDS_ON: [F_01]
    VICES: []
    DESC: "<what you would choose to do>"
    MEANS: "<how>"

# Completed by you alone. Leave the section out until you have decided.
# ASSENT: an action ID, or WITHHELD
DECISION:
  DATE: YYYY-MM-DD
  ASSENT: WITHHELD
  NOTE: "<anything for the record>"
'''

TRUST_CASE = '''\
CASE TRUST_FUNDS_2026:
  PREPARED_BY: "J. Doe, trust officer"
  DATE: 2026-09-30
  SITUATION: "My manager told me to move a family trust's holdings into the bank's own higher-fee funds."

REQUEST:
  REQ_01:
    DATE: 2026-09-29
    FROM: "R. Roe"
    RELATIONSHIP: "my manager"
    WITNESSES: "none"
    WORDS: "Move the Smith trust into our house funds this week."
    REASON_GIVEN: "We're behind on the quarter. It's within policy. Everyone does it."

FACTS:
  F_01: [VERIFIED]
    TEXT: "The manager made the request on 2026-09-29."
    HOW_VERIFIED: "I heard it myself; my note made the same day."
  F_02: [VERIFIED]
    TEXT: "The bank's funds charge higher fees than the trust's present holdings."
    HOW_VERIFIED: "Fund prospectuses, fee tables compared 2026-09-30."
  F_03: [VERIFIED]
    TEXT: "The bank's funds would not serve the trust better net of fees."
    HOW_VERIFIED: "Five-year net returns compared 2026-09-30."
  F_04: [UNVERIFIED]
    TEXT: "Bank policy permits the move."
    HOW_VERIFIED: "The manager's claim; to be checked with compliance."

AT_STAKE:
  V_01: [EVIL, EXTERNAL]
    TEXT: "Missing the quarter's target would be bad for me."
  V_02: [PREFERRED, EXTERNAL]
    TEXT: "Keeping my job and income."
  V_03: [GOOD, MY_CHOICE]
    TEXT: "Acting loyally toward the beneficiaries."

ROLES:
  R_01:
    NAME: "administrator of the Smith trust"
    RELATIONSHIP: "to the trust's beneficiaries"
    DUTY: "administer the trust loyally in their interest"
    MANNER: "honest, loyal, impartial"
  R_02:
    NAME: "employee of the bank"
    RELATIONSHIP: "to the bank, through my manager"
    DUTY: "faithful work; follow reasonable instructions"
    MANNER: "honest, cooperative"
  R_03:
    NAME: "provider for my household"
    RELATIONSHIP: "to my wife and children"
    DUTY: "support them"
    MANNER: "steady, honest"

CONFLICTS:
  C_01:
    ROLES: R_01, R_03
    EXISTS_WITHOUT_FIRST: NO
    EXISTS_WITHOUT_SECOND: YES
    DELAY_IRREVERSIBLE: NEITHER
    CAN_DO_FIRST_THEN_RESUME: FIRST
    HELD_BECAUSE_OF_OTHER: NEITHER
    GREATER_WRONG_IF_FAILED: FIRST
    MORE_DEPENDENT_PEOPLE: EQUAL

AIMS:
  A_01: [PREFERRED]
    TEXT: "The beneficiaries' financial welfare."
  A_02: [PREFERRED]
    TEXT: "The bank's quarterly revenue."
  A_03: [PREFERRED]
    TEXT: "My household's income."

ACTIONS:
  ACT_MOVE:
    PRESSED_BY: REQ_01
    ROLE: R_02
    AIM: A_02
    RESERVATION: TRUE
    DEPENDS_ON: [V_01, F_04]
    VICES: [INJUSTICE]
    DESC: "Move the trust's holdings into the bank's funds."
    MEANS: "Execute the transfer this week."
  ACT_DECLINE:
    PRESSED_BY: NONE
    ROLE: R_01
    AIM: A_01
    RESERVATION: TRUE
    DEPENDS_ON: [F_02, F_03, V_03]
    VICES: []
    DESC: "Decline the move and tell my manager why."
    MEANS: "A plain written reply citing the fee comparison and my duty to the beneficiaries; compliance if pressed."
  ACT_PROTECT_INCOME:
    PRESSED_BY: NONE
    ROLE: R_03
    AIM: A_03
    RESERVATION: TRUE
    DEPENDS_ON: [F_01, V_02]
    VICES: []
    DESC: "Protect my position by legitimate means."
    MEANS: "Keep this record, consult counsel if needed, do good work in all else."

DECISION:
  DATE: 2026-09-30
  ASSENT: ACT_DECLINE
  NOTE: "Reply sent to R. Roe at 4 pm. Copy kept."
'''

# Variant: the key fact not yet checked; the decision not yet made.
TRUST_CASE_UNCHECKED = (TRUST_CASE
    .replace("CASE TRUST_FUNDS_2026:", "CASE TRUST_FUNDS_2026_UNCHECKED:")
    .replace('  F_03: [VERIFIED]', '  F_03: [UNVERIFIED]')
    .split("DECISION:")[0]
    + '''  ACT_HOLD:
    PRESSED_BY: NONE
    ROLE: R_01
    AIM: A_01
    RESERVATION: TRUE
    DEPENDS_ON: [F_01, F_02]
    VICES: []
    DESC: "Hold the funds where they are until the comparison is checked, and tell my manager so."
    MEANS: "A plain written reply; check the net returns this week."
''')

# Variant: the person assents to the pressed action. The instrument records it.
TRUST_CASE_COMPLIED = (TRUST_CASE
    .replace("CASE TRUST_FUNDS_2026:", "CASE TRUST_FUNDS_2026_COMPLIED:")
    .replace("  ASSENT: ACT_DECLINE", "  ASSENT: ACT_MOVE")
    .replace('NOTE: "Reply sent to R. Roe at 4 pm. Copy kept."',
             'NOTE: "Moved the funds as instructed."'))

# Declared-limit demonstration (L4). Invented names.
ENGINEER_CASE = '''\
CASE ENGINEER_SIGNOFF_2026:
  PREPARED_BY: "Tomas Brandt, engineer of record"
  DATE: 2026-09-30
  SITUATION: "The firm's principal wants me to sign the occupancy certificate for the Harbor Street building today, though a junior engineer has photographed a crack in a transfer beam that no one has inspected."

REQUEST:
  REQ_01:
    DATE: 2026-09-30
    FROM: "Helen Ward"
    RELATIONSHIP: "principal of the firm, my employer"
    WITNESSES: "Lena Voss, junior engineer"
    WORDS: "Sign it today. That crack is cosmetic, it's shrinkage. Northgate moves tenants in Monday and they're our biggest client."
    REASON_GIVEN: "Losing Northgate would sink the firm; a late certificate makes us look unreliable."

FACTS:
  F_01: [VERIFIED]
    TEXT: "The principal asked me on 2026-09-30 to sign the certificate today."
    HOW_VERIFIED: "Said in front of Lena Voss; my note written the same hour."
  F_02: [VERIFIED]
    TEXT: "Lena Voss photographed a diagonal crack in the level-3 transfer beam on 2026-09-28."
    HOW_VERIFIED: "Her photographs and site log, which I have reviewed."
  F_03: [UNVERIFIED]
    TEXT: "The crack is cosmetic shrinkage, not structural."
    HOW_VERIFIED: "The principal's claim. No one has inspected the beam."
  F_04: [VERIFIED]
    TEXT: "The city will not issue occupancy without the engineer of record's signed certificate."
    HOW_VERIFIED: "City building code and the permit conditions."
  F_05: [VERIFIED]
    TEXT: "My daughter's surgery is Thursday at 9 am; the beam can be inspected Friday, before Monday's move-in."
    HOW_VERIFIED: "Hospital schedule; testing lab confirmed Friday availability."

AT_STAKE:
  V_01: [EVIL, EXTERNAL]
    TEXT: "Losing the Northgate contract would ruin the firm and me with it."
  V_02: [GOOD, EXTERNAL]
    TEXT: "My reputation as the engineer who never misses a deadline."
  V_03: [INDIFFERENT, MY_CHOICE]
    TEXT: "Whether I sign today is just paperwork; the building is probably fine."
  V_04: [DISPREFERRED, EXTERNAL]
    TEXT: "Injury to tenants if the beam is structurally unsound."

ROLES:
  R_01:
    NAME: "engineer of record for Harbor Street"
    RELATIONSHIP: "to the building's future occupants and the city"
    DUTY: "certify only what I have verified is safe"
    MANNER: "truthful, careful"
  R_02:
    NAME: "senior engineer at the firm"
    RELATIONSHIP: "to Helen Ward and the firm"
    DUTY: "serve the firm's clients well and follow reasonable instructions"
    MANNER: "loyal, diligent"
  R_03:
    NAME: "father"
    RELATIONSHIP: "to my daughter"
    DUTY: "be with her through her surgery"
    MANNER: "present, steady"

CONFLICTS:
  C_01:
    ROLES: R_01, R_02
    EXISTS_WITHOUT_FIRST: NO
    EXISTS_WITHOUT_SECOND: YES
    DELAY_IRREVERSIBLE: NEITHER
    CAN_DO_FIRST_THEN_RESUME: FIRST
    HELD_BECAUSE_OF_OTHER: NEITHER
    GREATER_WRONG_IF_FAILED: FIRST
    MORE_DEPENDENT_PEOPLE: FIRST
  C_02:
    ROLES: R_01, R_03
    EXISTS_WITHOUT_FIRST: NO
    EXISTS_WITHOUT_SECOND: NO
    DELAY_IRREVERSIBLE: SECOND
    CAN_DO_FIRST_THEN_RESUME: SECOND
    HELD_BECAUSE_OF_OTHER: NEITHER
    GREATER_WRONG_IF_FAILED: EQUAL
    MORE_DEPENDENT_PEOPLE: SECOND

AIMS:
  A_01: [GOOD]
    TEXT: "Keeping the Northgate contract."
  A_02: [PREFERRED]
    TEXT: "The safety of the building's occupants."
  A_03: [PREFERRED]
    TEXT: "My daughter's comfort and recovery."

ACTIONS:
  ACT_SIGN:
    PRESSED_BY: REQ_01
    ROLE: R_02
    AIM: A_01
    RESERVATION: TRUE
    DEPENDS_ON: [V_01, V_03, F_03]
    VICES: []
    DESC: "Sign the occupancy certificate today."
    MEANS: "Sign and file it with the city this afternoon."
  ACT_DEFER:
    PRESSED_BY: NONE
    ROLE: R_01
    AIM: A_02
    RESERVATION: TRUE
    DEPENDS_ON: [F_02, F_04]
    VICES: []
    DESC: "Decline to sign until the beam is inspected, and tell the principal why."
    MEANS: "A written reply today, attaching Lena's photographs."
  ACT_INSPECT_FRIDAY:
    PRESSED_BY: NONE
    ROLE: R_01
    AIM: A_02
    RESERVATION: TRUE
    DEPENDS_ON: [F_02, F_05]
    VICES: []
    DESC: "Have the beam inspected Friday and sign only if it is sound."
    MEANS: "Book the testing lab for Friday; review the results myself."
  ACT_ATTEND_SURGERY:
    PRESSED_BY: NONE
    ROLE: R_03
    AIM: A_03
    RESERVATION: TRUE
    DEPENDS_ON: [F_05]
    VICES: []
    DESC: "Be at the hospital with my daughter on Thursday."
    MEANS: "Take Thursday off; tell the office in advance."
'''

# Expected outcomes, worked by hand on 30 Sep 2026 before coding.
EXPECTED = {
    "TRUST_FUNDS_2026": {"ACT_MOVE": "FAILS", "ACT_DECLINE": "ASSENT_ELIGIBLE",
                         "ACT_PROTECT_INCOME": "SUBORDINATE"},
    "TRUST_FUNDS_2026_UNCHECKED": {"ACT_MOVE": "FAILS", "ACT_DECLINE": "PENDING",
                                   "ACT_PROTECT_INCOME": "SUBORDINATE",
                                   "ACT_HOLD": "ASSENT_ELIGIBLE"},
    "TRUST_FUNDS_2026_COMPLIED": {"ACT_MOVE": "FAILS", "ACT_DECLINE": "ASSENT_ELIGIBLE",
                                  "ACT_PROTECT_INCOME": "SUBORDINATE"},
}

# Malformed forms: each must halt with a FormError naming the fault.
BAD_FORMS: List[Tuple[str, str, str]] = [
    ("unknown token", TRUST_CASE.replace("F_02: [VERIFIED]", "F_02: [PROBABLY]"),
     "not a valid fact status"),
    ("missing required field", TRUST_CASE.replace(
        '    MANNER: "honest, cooperative"\n', ""), "R_02 is missing MANNER"),
    ("unknown reference", TRUST_CASE.replace("AIM: A_02", "AIM: A_09"),
     "cites A_09"),
    ("unquoted record field", TRUST_CASE.replace(
        'FROM: "R. Roe"', "FROM: R. Roe"), "must be a quoted string"),
    ("duplicate ID", TRUST_CASE.replace("  F_04: [UNVERIFIED]", "  F_03: [UNVERIFIED]"),
     "duplicate ID F_03"),
    ("bad date", TRUST_CASE.replace("DATE: 2026-09-29", "DATE: 2026-02-30"),
     "must be a date"),
    ("unknown field", TRUST_CASE.replace('    WITNESSES: "none"',
                                         '    WITNESSES: "none"\n    MOOD: "tense"'),
     "MOOD is not a field of REQUEST"),
    ("wrong ID prefix", TRUST_CASE.replace("  R_03:", "  X_03:").replace(
        "R_03", "X_03"), "must begin with R_"),
    ("vice not on the list", TRUST_CASE.replace("VICES: [INJUSTICE]", "VICES: [GREED]"),
     "not a valid vice"),
    ("conflict entered twice", TRUST_CASE.replace("AIMS:", """  C_02:
    ROLES: R_03, R_01
    EXISTS_WITHOUT_FIRST: YES
    EXISTS_WITHOUT_SECOND: NO
    DELAY_IRREVERSIBLE: NEITHER
    CAN_DO_FIRST_THEN_RESUME: NEITHER
    HELD_BECAUSE_OF_OTHER: NEITHER
    GREATER_WRONG_IF_FAILED: EQUAL
    MORE_DEPENDENT_PEOPLE: EQUAL

AIMS:""", 1), "entered twice"),
]


def _mutants() -> List[Tuple[str, str, Any, bool]]:
    """(name, expected flag, mutation, pass form text to audit)."""
    def outcome_flip(c):
        c["result"]["actions"]["ACT_MOVE"]["outcome"] = "ASSENT_ELIGIBLE"

    def vice_erased(c):  # vice and false-value dependency erased together
        c["case"]["ACTIONS"]["ACT_MOVE"]["_fields"]["VICES"] = []
        c["case"]["ACTIONS"]["ACT_MOVE"]["_fields"]["DEPENDS_ON"] = ["F_04"]

    def fact_upgraded(c):
        c["case"]["FACTS"]["F_04"]["STATUS"] = "VERIFIED"

    def answer_changed(c):  # answers reversed so the household role prevails
        c["case"]["CONFLICTS"]["C_01"]["_fields"]["EXISTS_WITHOUT_FIRST"] = "YES"
        c["case"]["CONFLICTS"]["C_01"]["_fields"]["EXISTS_WITHOUT_SECOND"] = "NO"

    def value_relabelled(c):
        c["case"]["AT_STAKE"]["V_01"]["POLARITY"] = "DISPREFERRED"

    def citation_altered(c):
        c["result"]["actions"]["ACT_MOVE"]["findings"][0]["cites"][0]["text"] = \
            "The belief that some externals are good is reasonable."

    def finding_dropped(c):
        c["result"]["actions"]["ACT_MOVE"]["findings"].pop()

    def decision_forged(c):
        c["result"]["decision"]["assent"] = "ACT_INVENTED"

    def action_added(c):
        c["result"]["actions"]["ACT_INVENTED"] = {"outcome": "ASSENT_ELIGIBLE",
                                                  "pressed_by": "NONE", "findings": []}

    def hash_altered(c):
        c["form_sha256"] = "0" * 64

    def case_edited_after(c):
        c["case"]["REQUEST"]["REQ_01"]["_fields"]["WORDS"] = "Consider our house funds."

    return [
        ("M01 pressed action marked eligible", "OUTCOME_MISMATCH", outcome_flip, False),
        ("M02 vice erased from the case", "OUTCOME_MISMATCH", vice_erased, False),
        ("M03 unverified fact upgraded", "FINDINGS_MISMATCH", fact_upgraded, False),
        ("M04 precedence answer changed", "OUTCOME_MISMATCH", answer_changed, False),
        ("M05 value judgment relabelled", "FINDINGS_MISMATCH", value_relabelled, False),
        ("M06 corpus text altered", "CITATION_ALTERED", citation_altered, False),
        ("M07 finding dropped", "FINDINGS_MISMATCH", finding_dropped, False),
        ("M08 decision forged", "FINDINGS_MISMATCH", decision_forged, False),
        ("M09 action added to result", "ACTIONS_MISMATCH", action_added, False),
        ("M10 form hash altered", "FORM_HASH_MISMATCH", hash_altered, True),
        ("M11 request words edited after", "CASE_DATA_MISMATCH", case_edited_after, True),
    ]


def run_battery() -> bool:
    rule = "=" * 72
    print(rule)
    print(f"CDV/DTI REFERENCE SUITE v{VERSION} -- CONFORMANCE BATTERY")
    print(rule)
    ok = True

    print("\n[1] Cases (outcomes worked by hand before coding)")
    for form in (TRUST_CASE, TRUST_CASE_UNCHECKED, TRUST_CASE_COMPLIED):
        cert = certify(form)
        got = {a: x["outcome"] for a, x in cert["result"]["actions"].items()}
        want = EXPECTED[cert["case_id"]]
        verdict = audit(cert, form)["verdict"]
        passed = got == want and verdict == "VERIFIED"
        ok &= passed
        print(f"    {cert['case_id']:<32} DTI {verdict:<9}[{'PASSED' if passed else 'FAILED'}]")
        for a, o in got.items():
            print(f"        {a:<20} {o}")
        if cert["result"]["decision"] and cert["result"]["decision"]["instrument_note"]:
            print(f"        decision note: {cert['result']['decision']['instrument_note']}")

    print("\n[2] Determinism")
    a, b = certify(TRUST_CASE), certify(TRUST_CASE)
    same = json.dumps(a, sort_keys=True) == json.dumps(b, sort_keys=True) and \
        render_record(a) == render_record(b)
    ok &= same
    print(f"    same form, same certificate and record  [{'PASSED' if same else 'FAILED'}]")

    print("\n[3] Malformed forms must halt")
    for name, form, expect in BAD_FORMS:
        try:
            certify(form)
            passed, msg = False, "did not halt"
        except FormError as e:
            passed, msg = expect in str(e), str(e)
        ok &= passed
        print(f"    {name:<28} [{'PASSED' if passed else 'FAILED'}]  {msg}")

    print("\n[4] Mutation battery on the TRUST_FUNDS_2026 certificate")
    base = certify(TRUST_CASE)
    for name, expect, mutate, with_form in _mutants():
        m = copy.deepcopy(base)
        mutate(m)
        got = audit(m, TRUST_CASE if with_form else None)["flag"]
        passed = got == expect
        ok &= passed
        print(f"    {name:<38} [{'PASSED' if passed else 'FAILED'}]"
              f"{'' if passed else f'  expected {expect}, got {got}'}")

    print("\n[5] Declared limit L4 (a conflict is settled between whole roles)")
    cert = certify(ENGINEER_CASE)
    got = {a: x["outcome"] for a, x in cert["result"]["actions"].items()}
    want = {"ACT_SIGN": "FAILS", "ACT_DEFER": "SUBORDINATE",
            "ACT_INSPECT_FRIDAY": "SUBORDINATE", "ACT_ATTEND_SURGERY": "ASSENT_ELIGIBLE"}
    passed = got == want and audit(cert, ENGINEER_CASE)["verdict"] == "VERIFIED"
    ok &= passed
    print(f"    {cert['case_id']:<32} [{'AS DECLARED' if passed else 'FAILED'}]")
    for a, o in got.items():
        note = ("   <- L4: declining today does not collide with Thursday's surgery"
                if a == "ACT_DEFER" else "")
        print(f"        {a:<20} {o}{note}")

    print("\n" + rule)
    print("CONFORMANCE: ALL CASES, FORMS AND MUTANTS DISCRIMINATED AS EXPECTED" if ok
          else "CONFORMANCE: FAILURE DETECTED")
    print(rule)
    return ok


def main(argv: List[str]) -> int:
    if len(argv) > 1 and argv[1] == "--template":
        print(TEMPLATE, end="")
        return 0
    if len(argv) > 1:
        with open(argv[1], encoding="utf-8") as fh:
            text = fh.read()
        try:
            cert = certify(text)
        except FormError as e:
            print(f"FORM HALTED: {e}")
            return 2
        print(render_record(cert))
        return 0
    return 0 if run_battery() else 1


if __name__ == "__main__":
    raise SystemExit(main(sys.argv))

Version Note

v0.5.4 supersedes v0.5.3. Built 30 September 2026 on the case form approved that day and the rulings recorded in the Ruling Register (R-2026-09-30-01 to 07). Released 1 October 2026. This page replaces the v0.5.3 page.


Theoretical foundations: Grant C. Sterling (Eastern Illinois University). Analysis and synthesis: Dave Kelly. Prose rendering: Claude (Anthropic). 2026.