docs(fiqh): the cash posture residual is recorded, not merely acknowl… - #713
Merged
Conversation
…edged (#691, part 2 of 2) Closes #691. The last two acceptance boxes: `keel brokers list` and the `docs/fiqh-basis.md` open question. THE PIN DID NOT FAIL WHEN IT SHOULD HAVE, AND THAT IS THE FINDING. `test_the_unprovable_half_of_the_cash_posture_is_stated_not_hidden` promised in its own docstring that "closing that needs a human attestation, not another adapter read -- and if one is ever built, this fails again". #691 built one. **The pin passed**, and the issue's own acceptance criterion predicted a failure that never came. Nothing in the pin observed the attestation, so it repeated its FIRST failure mode exactly: that form pinned `_sell_base_size`'s docstring as a proxy and held while the paragraph it guarded went false. A promise in a docstring is not a pin, and prose that matches is not prose that is true. So this re-pointing reaches for the MECHANISM rather than for sentences. Four assertions that each go false if something real is removed: * `CashPostureState` must carry no `CONFIRMED` -- adding one would mean keel believes something can AFFIRM a spot cash account, which is the claim the doc spends a section refuting. * `ATTESTATION_TTL_SEC > 0` -- without expiry, "the operator states it and keel records it" degrades to "keel remembers what someone said once". * `cash_posture` must appear in `guards.py` -- with no rail reading the record, "an unattested posture vetoes live entries" is a sentence about nothing. * the stale "a human attestation this repository has not yet built" must be GONE -- the specific prose regression, caught by string rather than hoped about. THE DOC. Two passages were wrong on their facts, not merely dated: one said keel "has no cash-account posture check on Coinbase", which #666 added, and the other said the attestation was unbuilt. Both are corrected, and the section now states the three load-bearing properties (no confirmable state, a 90-day expiry, a claim bound to a credential) followed by the sentence to keep if only one survives: **keel still cannot verify this, and neither can the venue.** What changed is that the trust is now dated, revocable and written down, rather than assumed. THE BLOCK. `keel brokers list` gains a THIRD block after declarations and readiness, which follows this codebase's own argument rather than a preference: `venue_readiness` renders separately because merging two questions "would re-blur exactly the distinction #233 exists to draw". Posture is a third question -- readiness asks whether this CREDENTIAL may trade, posture asks whether this ACCOUNT can borrow. It ends on its own honesty line, and a test forbids the words "verified", "confirmed", "checked" and "proven" anywhere in it: every other readiness surface reports something it checked, and this one cannot. Read-only and best-effort, `_readiness_rows`' discipline -- a display command must not create a database, and a pre-v18 file renders an empty block rather than failing. Tests written first: 9 on the block, plus the re-pointed fiqh pin. Mutation-verified, 6 mutants. One SURVIVED: replacing `sorted` with `reversed` produced identical output because the fixture had only two venues in reverse alphabetical order. Three venues now, in an order where reversing is not sorting. Closes #691 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzuKAe2RVrPt9acVAWjRyL
…tabase (#691) Three findings from the review of #713. The first two were both already argued, in a docstring one function away from the code that violated them. 1. `_cash_posture_records` used `keel.data.db.connect` -- the read-WRITE opener. `venue_readiness._read_only_trade_scope` carries a marked paragraph saying "**Not `keel.data.db.connect`, and that is the whole point of this function**", because `connect` runs `PRAGMA journal_mode = WAL` on whatever it opens. That is a WRITE, from a read-only informational command, and it can leave `-wal`/`-shm` sidecars behind. The `Path.exists()` guard stopped file CREATION and did nothing about the write. Now the `mode=ro` URI shape (#610's seam), with the existence check still in front of it because `mode=ro`'s own refusal is an exception and this path wants an answer. `test_reading_the_records_never_writes_to_the_database` opens the file in DELETE journal mode, reads, and asserts the mode is unchanged and no sidecar appeared -- the observable, rather than an assertion about which function was called. 2. It returned `[]` for BOTH "nothing is attested" and "could not read this database", so the block would advise `keel posture attest --spot-cash` about a record that was fine and merely unread. That harm is named verbatim in the same docstring, and it is WORSE here than for trade scope: re-attesting resets `attested_ts` and `attest_due_ts`, and the TTY gate would ask the operator to affirm a cash account they may not have re-checked. A display bug would become a prompt to make an unverified claim. Now `(records, unreadable)`, the shape that function already uses, and the unreadable block deliberately does NOT print the attest command -- withholding that prompt is the entire point of separating the two answers. "No database at all" stays NOT-unreadable: that is a true statement about the machine rather than an admission of ignorance. 3. `_utc_day` duplicated `posture.py::_utc_date` byte for byte. Imports the existing one -- two formatters for one record type drift the day either gains a timezone or a width. Tests written first, red before green: 5 new, all failing for the right reasons. Mutation-verified, 4 mutants, all killed: the read-write opener restored; the unreadable flag collapsed to False; a missing database reported as unreadable; the unreadable block advising an attestation anyway. Refs #691 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzuKAe2RVrPt9acVAWjRyL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…edged (#691, part 2 of 2)
Closes #691. The last two acceptance boxes:
keel brokers listand thedocs/fiqh-basis.mdopen question.THE PIN DID NOT FAIL WHEN IT SHOULD HAVE, AND THAT IS THE FINDING.
test_the_unprovable_half_of_the_cash_posture_is_stated_not_hiddenpromised in its own docstring that "closing that needs a human attestation, not another adapter read -- and if one is ever built, this fails again". #691 built one. The pin passed, and the issue's own acceptance criterion predicted a failure that never came.Nothing in the pin observed the attestation, so it repeated its FIRST failure mode exactly: that form pinned
_sell_base_size's docstring as a proxy and held while the paragraph it guarded went false. A promise in a docstring is not a pin, and prose that matches is not prose that is true.So this re-pointing reaches for the MECHANISM rather than for sentences. Four assertions that each go false if something real is removed:
CashPostureStatemust carry noCONFIRMED-- adding one would mean keel believes something can AFFIRM a spot cash account, which is the claim the doc spends a section refuting.ATTESTATION_TTL_SEC > 0-- without expiry, "the operator states it and keel records it" degrades to "keel remembers what someone said once".cash_posturemust appear inguards.py-- with no rail reading the record, "an unattested posture vetoes live entries" is a sentence about nothing.THE DOC. Two passages were wrong on their facts, not merely dated: one said keel "has no cash-account posture check on Coinbase", which #666 added, and the other said the attestation was unbuilt. Both are corrected, and the section now states the three load-bearing properties (no confirmable state, a 90-day expiry, a claim bound to a credential) followed by the sentence to keep if only one survives: keel still cannot verify this, and neither can the venue. What changed is that the trust is now dated, revocable and written down, rather than assumed.
THE BLOCK.
keel brokers listgains a THIRD block after declarations and readiness, which follows this codebase's own argument rather than a preference:venue_readinessrenders separately because merging two questions "would re-blur exactly the distinction #233 exists to draw". Posture is a third question -- readiness asks whether this CREDENTIAL may trade, posture asks whether this ACCOUNT can borrow. It ends on its own honesty line, and a test forbids the words "verified", "confirmed", "checked" and "proven" anywhere in it: every other readiness surface reports something it checked, and this one cannot.Read-only and best-effort,
_readiness_rows' discipline -- a display command must not create a database, and a pre-v18 file renders an empty block rather than failing.Tests written first: 9 on the block, plus the re-pointed fiqh pin.
Mutation-verified, 6 mutants. One SURVIVED: replacing
sortedwithreversedproduced identical output because the fixture had only two venues in reverse alphabetical order. Three venues now, in an order where reversing is not sorting.Closes #691
Claude-Session: https://claude.ai/code/session_01NzuKAe2RVrPt9acVAWjRyL
What & why
Tests-first evidence
Gates (all must pass)
uv run ruff checkcleanuv run mypycleanuv run pytest -qgreenScope check
leave checked only if true, and if so: cite the source and open the discussion
BEFORE review (CONTRIBUTING.md, "Governance: rulings vs. machinery").