Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions docs/fiqh-basis.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,18 +306,23 @@ Stated, not hidden — each is a place where keel's encoded behaviour could be w
Whether "no underlying purpose" is disqualifying "is exactly the kind of judgement the
screen defers to a human" (`docs/experiments/2026-07-20-candidate-universe.md`) — deferred,
not decided.
- **Long-only is enforced where keel DECIDES, not where it SETTLES.** Everything under
"Long-only" above governs what keel chooses. It does not govern the quantity that reaches
the venue. A SELL is never clamped to the account's available base (#667): the exit sells
what the ledger believes is held, and the ledger drifts from the account through fees taken
in the base asset, partial fills (`filled_quantity` is recorded nowhere on this
deployment), and out-of-band operator transfers. A resting stop leg can outlive the
position that justified it, because no sweep cancels a SELL whose position is gone (#668).
On a cash account each of these is a rejected order — bad, but not a fiqh failure. On a
margin-enabled account the venue fills the difference as a short, and keel has no
cash-account posture check on Coinbase (#666; the check exists only on the Alpaca adapter).
So the long-only ruling above is sound at the decision layer and **not yet closed at the
venue boundary**. Named here rather than left to be discovered.
- **The venue boundary is closed by machinery that can decline to answer (#666).** This entry
was written when the boundary was open, and #667/#668 have since closed the three paths that
were named here — the SELL is clamped to the venue's holding, rail 21 refuses an order
against an empty one, and `sweep_orphan_brackets` cancels a protective order whose position
has left. What is *not* closed is the assumption every one of them rests on. All three fail
OPEN on an unreadable balance, deliberately and correctly: refusing a SELL over a balance
endpoint that went quiet would strand positions that wanted out. So a venue that stops
answering does not produce a refusal, it produces the old behaviour — an exit sized from
keel's own ledger.

That is survivable **only while the account cannot go short**, and keel does not check that
it cannot. `verify_cash_account` exists on the Alpaca adapter alone; Coinbase has no
equivalent read, and #666 must first establish whether Advanced Trade exposes an account
posture at all — an operator attestation, on rail 17's pattern, is a legitimate answer if it
does not. Until then the layered defence is sound whenever the venue answers, and rests on an
unverified premise whenever it does not. Named here rather than left to be inferred from
three closed issues.
- **ZEC and the rest of the deferrals.** The candidate-universe record lists the open
questions the attestation step has to answer and "which this agent must not answer".

Expand Down
50 changes: 32 additions & 18 deletions tests/test_fiqh_basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,9 @@ def test_the_readme_links_the_document():

_HADITH_NOT_IN_KB = "That reference is not extracted in this repository's knowledge base"

_SELL_SIZE_CONTRACT = "it can only ever make an exit MORE likely to be accepted"
_CASH_POSTURE_CHECK = "verify_cash_account"

_VENUE_BOUNDARY_PREMISE = "rests on an unverified premise whenever it does not"


def test_the_long_only_ruling_is_pinned_two_sided_to_the_code_that_enforces_it():
Expand Down Expand Up @@ -461,24 +463,36 @@ def test_the_hadith_reference_is_marked_as_outside_the_knowledge_base():
)


def test_the_venue_boundary_gap_is_stated_not_hidden():
"""Long-only holds where keel decides; the doc must admit it is open where keel settles.
def test_the_venue_boundary_premise_is_stated_not_hidden():
"""The boundary is closed by machinery that fails OPEN, over an account nobody checked.

This test's first form pinned `_sell_base_size`'s docstring as a proxy for "the SELL is not
clamped", on the theory that #667 landing would break it and force the doc to be updated
with the code. It did not: #667 clamped at intent construction and left that docstring
intact, so the pin held while the paragraph it guarded became false. **A proxy is only a pin
while the thing it stands for and the thing it matches move together**, and this one stopped.

A SELL is never clamped to the account's available base, so fee dust, an unrecorded
partial fill, or an out-of-band transfer can send an order for more than is held (#667).
That is `bay' ma la yamlik` reached by arithmetic. Pinned to `_sell_base_size`'s own
contract: while that function's only job is to make an exit more likely to be accepted, it
reads no balance, and the open question must stay in the doc. When #667 lands this test
fails -- which is the point: the doc gets updated with the code, not after it.
Re-anchored to the fact that actually remains open. `_clamp_to_held`, rail 21 and
`sweep_orphan_brackets` all fail open on an unreadable balance -- correctly, since refusing
a SELL over a quiet endpoint strands positions that wanted out -- so the defence rests on
the account being unable to go short, and nothing verifies that on the venue that trades
live. When #666 gives the coinbase adapter a posture read, this test fails, and it fails
because the premise changed rather than because a sentence was reworded.
"""
doc = _unwrapped(_doc())
assert "not yet closed at the venue boundary" in doc, (
f"{_DOC} must state that long-only is unclosed at the venue boundary -- the ruling is "
"sound at the decision layer and the operational gap is not the reader's to discover"
)
for issue in ("#666", "#667", "#668"):
assert issue in doc, f"{_DOC} must name {issue} as the open venue-boundary work"
assert _SELL_SIZE_CONTRACT in _rel("keel/execution/executor.py"), (
"keel/execution/executor.py's `_sell_base_size` must still carry its stated contract; "
f"if the exit now clamps to a real balance, {_DOC}'s open question is stale"
assert _VENUE_BOUNDARY_PREMISE in doc, (
f"{_DOC} must state that the long-only defence rests on an unverified premise when the "
"venue does not answer -- a layered defence described without its assumption reads as "
"settled, and the assumption is the part still open"
)
assert "#666" in doc, f"{_DOC} must name #666 as the open venue-boundary work"
coinbase = "packages/keel-broker-coinbase/keel_broker_coinbase/adapter.py"
assert _CASH_POSTURE_CHECK not in _rel(coinbase), (
f"{coinbase} now has a {_CASH_POSTURE_CHECK!r} read -- #666 has landed and the open "
f"question in {_DOC} is stale. Update the doc, then re-point this test."
)
alpaca = "packages/keel-broker-alpaca/keel_broker_alpaca/adapter.py"
assert _CASH_POSTURE_CHECK in _rel(alpaca), (
f"{_DOC} says the posture check exists on the Alpaca adapter alone; {alpaca} must still "
"carry it, or the doc is describing a check no adapter has"
)