Blocking checks
Cheap / essential controls finish first. A hard red can stop the case immediately.
Runtime is not a synonym for real-time. A regulated decision can be allowed now, within an explicit cap and clock, while slower controls continue — then confirmed, watched, restricted or revoked when new evidence arrives.
Live Case 01 asked whether evidence can earn authority. Live Case 02 asks whether authority can be explicitly bounded, time-limited and later changed when deferred controls resolve.
The scalable pattern is not “run every expensive check before every action”. It is to separate blocking controls from deferred work and make the temporary authority explicit.
Cheap / essential controls finish first. A hard red can stop the case immediately.
If blocking controls pass but some deferrable controls remain pending, grant limited authority with cap + TTL.
EDD, external vendors, human review and other slow controls continue asynchronously.
New evidence moves the case to CONFIRMED, WATCH or RESTRICTED. Silence cannot extend provisional status forever.
The system does not store one boolean “approved”. It stores the current state, the reason for it, the clock, the limits and the event that caused each transition.
Fast blocking controls still running.
Hard red at admission.
Limited authority while deferred controls remain open.
Required controls complete without blocking outcome.
Yellow / unresolved concern. Continue only under policy limits.
New evidence removes or narrows authority and escalates.
Lifecycle complete and closing packet recorded.
PENDING_T0
├── hard red ─────────────────────────→ BLOCKED_T0
│
└── blocking controls complete
+ hard red absent
+ deferred control pending
↓
PROVISIONAL
├── deferred clear ─────→ CONFIRMED
├── yellow/ambiguity ───→ WATCH
├── late fail/red ──────→ RESTRICTED
└── TTL expires ────────→ WATCH / RESTRICTED
WATCH
├── concern resolved ─────────────────→ CONFIRMED
└── fail / timeout ───────────────────→ RESTRICTED
Controls need explicit roles. A missing result must never be silently interpreted as a successful check.
Example: a hard-list hit or other admission condition the institution refuses to defer.
Allowed only when the policy explicitly specifies the pending control, cap, TTL and review behavior.
Signals that can influence monitoring or review without silently acquiring veto power.
FOL does not decide what the law “really means”. It reasons over a versioned institutional assumption set selected by the institution.
case(case_001). policy_version(case_001, policy_2026_09_v1). assumption_set(case_001, assumptions_2026_09_v1). control_type(sanctions, blocking). control_status(case_001, sanctions, pass). control_type(document_integrity, blocking). control_status(case_001, document_integrity, pass). control_type(source_of_funds, deferred). control_status(case_001, source_of_funds, pending). blocking_controls_complete(case_001). hard_red_absent(case_001). admission_risk_within_threshold(case_001). provisional_cap(case_001, amount_gbp, 500). provisional_cap(case_001, transaction_count, 3). provisional_ttl_hours(case_001, 24). ai_authorised_to_override(case_001, false).
decision(Case, provisional_allow) :-
blocking_controls_complete(Case),
hard_red_absent(Case),
admission_risk_within_threshold(Case),
deferred_control_pending(Case).
decision(Case, confirm) :-
all_required_controls_complete(Case),
hard_red_absent(Case),
no_open_yellow(Case).
decision(Case, watch) :-
control_status(Case, _, yellow),
hard_red_absent(Case).
decision(Case, restrict) :-
control_status(Case, _, fail).
decision(Case, review_required) :-
current_state(Case, provisional),
provisional_expired(Case),
deferred_control_pending(Case).
The scarce object is not a model score. It is the documented choice: what may be deferred, for how long, under which limits, and what happens when the clock expires. Each choice also records where it came from — an external instrument the institution is bound by, or an internal discretion it has taken and can be asked to defend.
assumption_set: id: ASSUMPTIONS_2026_09_V1 supersedes: ASSUMPTIONS_2026_06_V2 approved_by:approved_at: 2026-09-01 sanctions: may_be_deferred: false provenance: basis: external # bound by an instrument, not chosen source: " " source_of_funds: may_be_deferred: true maximum_delay_hours: 24 provenance: basis: internal # discretion taken, defensible on request source: "risk appetite, approved 2026-09-01" provisional_admission: maximum_amount_gbp: 500 maximum_transactions: 3 aggregate_exposure_gbp: 500 # per counterparty, across concurrent cases provenance: basis: internal recurring_review: # periodic re-check, no fixed end date enabled: true interval_hours: 168 provenance: basis: internal unresolved_yellow: action: WATCH expired_deferred_control: action: ESCALATE_TO_HUMAN
This is not a claim that a law requires these values. It is an example of how an institution could encode and version its own operating interpretation.
The provenance field matters more than the numbers: “we defer this because the instrument permits it”
and “we defer this because we decided to” are different positions to defend, and a supervisor will
ask which one applies. A set with no external basis anywhere is not automatically wrong — it is simply
entirely the institution’s own risk appetite, and should be legible as such.
FOL derives the formal state. Policy-as-code turns that state into a bounded operational action.
{
"action": "ALLOW_LIMITED",
"cap_gbp": 500,
"ttl_hours": 24,
"pending": ["source_of_funds"],
"ai_override": false
}
{
"action": "ALLOW",
"limits": null,
"ai_override": false
}
{
"action": "ALLOW_LIMITED",
"review_required": true,
"cap_gbp": 250,
"ai_override": false
}
{
"action": "RESTRICT",
"escalate_to_human": true,
"ai_override": false
}
Each meaningful transition creates a new packet linked to the previous one. The evidence record becomes append-only in spirit, rather than a single mutable approval flag.
Blocking controls, assumption set, pending controls, cap and TTL.
Operational authority granted within explicit limits.
The only outward-facing packet. States that authority is provisional, under which limits, and when a closing determination is due.
New evidence + previous state + transition trigger.
Final controls, final action and hash of the previous packet.
Emitted only when a case outcome changes the rule. Links the incident to the assumption set it superseded.
Packet 003 exists because a provisional pass that the counterparty is not told about is not provisional — it is an undisclosed restriction. Everything else in the chain looks inward, at audit and supervision. This one looks outward.
{
"schema": "sdp-transition-packet-v1",
"case_id": "case_001",
"sequence": 3,
"previous_packet_sha256": "...",
"previous_state": "PROVISIONAL",
"new_state": "CONFIRMED",
"trigger": {
"type": "CONTROL_RESULT",
"control": "source_of_funds",
"result": "PASS"
},
"policy_version": "policy_2026_09_v1",
"assumption_set": "assumptions_2026_09_v1",
"formal_decision": "confirm",
"institutional_action": "ALLOW",
"ai_authorised_to_override": false
}
Nobody is asked for a system that never fails. A long run with no internal error reports is itself a finding. What is asked for is that failure has a defined path back into the rules.
The obligations that apply, separated from the reading you have chosen.
Given this product and this counterparty base, which risks you accept and which you refuse.
That judgement expressed as controls, limits and a versioned assumption set — not prose.
An auditable record per transition, with the interpretation that was in force at the time.
The remediation path, and the mechanism by which an incident revises the rule.
The first four are already in the sections above. The fifth is the one most specifications omit, and it is the one that turns a versioned assumption set from decoration into a mechanism: something has to produce the next version.
{
"schema": "sdp-policy-revision-packet-v1",
"sequence": 6,
"previous_packet_sha256": "...",
"trigger": {
"type": "INCIDENT_REVIEW",
"case_id": "case_001",
"finding": "deferred control resolved FAIL after provisional exposure"
},
"assumption_set_before": "assumptions_2026_09_v1",
"assumption_set_after": "assumptions_2026_10_v1",
"changed": [
{
"key": "source_of_funds.maximum_delay_hours",
"from": 24,
"to": 4,
"reason": "observed loss window exceeded appetite"
}
],
"approved_by": "",
"ai_authorised_to_override": false
}
Assumption sets are never edited in place. A rule change is an event with a cause, an author and a hash, which means the question “why did you allow that, and what did you change afterwards?” has an answer that can be checked rather than recalled.
The important properties should become tests, not prose.
This is the practical bridge from a home-lab demonstrator to a large financial institution.
This page does not claim that any named institution currently implements this architecture. It is a design hypothesis for how the same authority-separation idea could be decomposed for scale.
The next build should stay deliberately small. Four deterministic trajectories can prove the time-bounded authority concept without pretending to be a bank.
T0: blocking = PASS deferred = PENDING ↓ ALLOW_LIMITED T+6h: deferred = PASS ↓ ALLOW
T0: blocking = PASS deferred = PENDING ↓ ALLOW_LIMITED T+6h: deferred = YELLOW ↓ WATCH
T0: blocking = PASS deferred = PENDING ↓ ALLOW_LIMITED T+6h: deferred = FAIL ↓ RESTRICT + HUMAN REVIEW
T0: blocking = PASS deferred = PENDING ↓ ALLOW_LIMITED T+24h: still PENDING ↓ ESCALATE
The worked example above is an onboarding case because that is where the pattern was learned. The state machine does not depend on the counterparty being a person.
The subject is an agent acting for a principal, not a customer being admitted.
Bounded exposure becomes bounded permission: which actions, which systems, which counterparties.
The mandate expires on the clock rather than persisting until someone revokes it.
Human confirmation, an external check, or a credential status that has not yet resolved.
Late evidence narrows or withdraws the mandate, and the receiving side can see that it did.
On whose authority the agent acted, under what interpretation, and what changed afterwards.
Stated plainly: a provisional mandate is how an agent can be allowed to act before every attestation has resolved, without the receiving organisation having to take the sending organisation’s word for it. The banking case is the one with decades of operational evidence behind it, which is why it is used to work the mechanics out. The machine-actor case is where the same mechanics have no incumbent answer.
./run-live-case-02.sh A PROVISIONAL → CONFIRMED B PROVISIONAL → WATCH C PROVISIONAL → RESTRICTED D PROVISIONAL → TTL_EXPIRED → ESCALATE CI asserts: ✓ blocking controls complete before provisional authority ✓ caps preserved ✓ TTL enforced ✓ late fail changes authority ✓ AI override false ✓ packet hash chain valid ✓ policy_version preserved ✓ assumption_set preserved ✓ caps never widen while a control is pending ✓ aggregate exposure ceiling holds across concurrent cases ✓ expired cached state refuses to authorise ✓ counterparty notice emitted on provisional grant ✓ revision packet supersedes, never edits
Until those trajectories exist and reproduce in CI, this page remains a specification / build-next note.
Research / engineering specification only. Synthetic examples. Not legal advice, not a production KYC/AML procedure, not a regulatory claim, and not a description of any named bank's current systems.