Skip to content

Commit c27b769

Browse files
Merge pull request #208 from engineeredsoftware/v45/gate-5-btc-settlement-state-machine
V45 Gate 5: BTC Settlement State Machine
2 parents 6e8d98e + d308ecf commit c27b769

3 files changed

Lines changed: 249 additions & 0 deletions

File tree

BITCODE_SPEC_V45_NOTES.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,90 @@ deterministic fixed-point measurement weighting, measuremint/range conservation,
335335
zero-cell tail posture, BTC-before-rights law, and source-to-shares as
336336
post-settlement contributor allocation rather than BTD size.
337337

338+
## V45 protocol atom 4: BTC settlement state machine
339+
340+
Audit classification: V44 correctly names estimates, quotes, observed payment,
341+
final settlement, contributor allocation, delivery, and repair as distinct
342+
economic labels. The implementation already separates BTC fee quotes, PSBT
343+
handoff, broadcast observation, finality receipts, settlement unlock,
344+
source-to-shares, and compensation statements. V45 must make BTC settlement
345+
law explicit so that a quote, a broadcast transaction, an observed payment,
346+
confirmed finality, BTD rights transfer, source delivery, refund/escalation,
347+
and contributor compensation cannot be collapsed by an interface, pipeline,
348+
or operator.
349+
350+
Protocol-law statement:
351+
352+
BTC is Bitcode settlement money and payment truth. A BTC quote is a
353+
source-safe procurement offer bound to a reviewed Need, selected Fit set,
354+
synthesized Need-Fit AssetPack, BTD scalar-volume/range, deterministic
355+
share-to-fee policy, wallet authority, network, expiry, and proof roots. A BTC
356+
payment is not final merely because a transaction is prepared, signed,
357+
broadcast, visible in mempool, or observed by a provider. Source unlock,
358+
rights-bearing BTD transfer, delivery, and contributor compensation require
359+
confirmed BTC finality, quote/payment conservation, ledger/database/storage
360+
readback, and repair-free settlement receipts.
361+
362+
The canonical BTC settlement state machine is:
363+
364+
| State | Meaning | Required inputs | Output posture |
365+
| --- | --- | --- | --- |
366+
| `btc-not-quoteable` | No reviewed Need-Fit quote can exist yet. | reviewed Need missing, selected Fit set missing, BTD quote posture missing, wallet authority missing, or policy blocker | no BTC quote claim |
367+
| `btc-quote-issued` | Deterministic sats quote is created for the Reader. | quote root, BTD scalar-volume/range roots, deterministic share-to-fee policy root, network, expiry, budget policy | source-safe offer only; no payment |
368+
| `btc-quote-accepted` | Reader and organization authority accept the quote before expiry. | accepted quote root, buyer authorization root, wallet authority root, budget approval root when required | PSBT preparation admitted |
369+
| `btc-quote-inactive` | Quote is expired, superseded, failed, revoked, or mismatched. | inactive quote root and reason | payment against this quote cannot unlock rights |
370+
| `btc-wallet-ready` | Reader wallet can sign without server custody. | wallet signer session, network match, PSBT signing capability, no-server-custody proof | PSBT handoff may proceed |
371+
| `btc-psbt-prepared` | Settlement transaction is prepared but unsigned. | accepted quote, wallet-ready proof, terminal journal root, PSBT root | no payment observation |
372+
| `btc-psbt-signed` | Reader has signed the transaction. | prepared PSBT root, wallet authorization proof, signed PSBT root | broadcast admitted; no finality |
373+
| `btc-broadcast-submitted` | Signed transaction has been submitted to the Bitcoin network. | signed transaction root, txid, network root, provider observation root | observed payment pending finality |
374+
| `btc-payment-observed` | Debit, credit, txid, amount, payer, payee, and network are observed. | payment observation root, expected sats, observed debit sats, observed credit sats, txid | source remains locked; finality pending |
375+
| `btc-payment-mismatch-repair-required` | Observed payment does not conserve the accepted quote or violates wallet/network policy. | mismatch proof root, repair blocker root | repair, replacement, refund, or escalation only |
376+
| `btc-finality-confirmed` | Bitcoin finality satisfies the configured confirmation/finality policy. | finality receipt, confirmations, txid, block/reorg readback, quote/payment conservation root | settlement can authorize BTD rights transfer |
377+
| `btc-replaced-reorged-or-failed` | Transaction was replaced, reorged out, failed, or became non-final. | replacement, reorg, or failure root | source remains locked; repair or refund/escalation required |
378+
| `btc-settlement-finalized` | Quote, payment, finality, ledger, database, object storage, and source-safe readback agree. | finality root, ledger/database/storage reconciliation root, terminal journal root, settlement unlock root | BTD rights transfer and delivery unlock may proceed |
379+
| `btc-contributor-compensation-routable` | Final settlement is available to source-to-shares allocation and contributor statements. | finalized settlement root, BTD range slices, source-to-shares proof, contributor roots | compensation statements and treasury routes may be issued |
380+
| `btc-refund-or-escalation-required` | A payment or quote cannot be accepted as final settlement, and value movement or operator review is required. | refund/escalation case root, payment evidence, policy reason | no rights, no source unlock, no contributor compensation |
381+
| `btc-settlement-repair-required` | Any quote, wallet, PSBT, broadcast, observation, finality, readback, unlock, or compensation proof is missing or stale. | repair blocker root | fail-closed repair only |
382+
383+
Settlement law:
384+
385+
- A quote is not an estimate. It must be denominated in BTC sats, bind the
386+
BTD scalar-volume/range and deterministic share-to-fee policy, name its
387+
network and expiry, and carry a source-safe quote root.
388+
- A quote must be accepted before PSBT preparation. Expired, superseded,
389+
failed, revoked, wrong-network, wrong-wallet, or mismatched quotes cannot
390+
unlock rights even if a payment is later observed.
391+
- Bitcode must never take server custody of wallet private material. The
392+
Reader signs through a wallet session that proves network, capability,
393+
authorization, and no-server-custody posture.
394+
- Prepared, signed, broadcast, and observed states are not final settlement.
395+
They cannot transfer BTD rights, unlock source, deliver a pull request, or
396+
allocate contributor compensation.
397+
- Payment observation must conserve the accepted quote: expected sats,
398+
observed debit sats, observed credit sats, payer, payee, network, txid, and
399+
quote root must reconcile. Drift enters repair, refund, or escalation.
400+
- BTC finality requires the configured confirmation/finality policy and must
401+
fail closed on replacement, reorg, failure, stale provider readback, or
402+
missing ledger/database/object-storage reconciliation.
403+
- Refund or escalation is a repair path, not a successful purchase path. It
404+
does not transfer BTD rights, disclose source, satisfy contributor
405+
compensation, or convert an inactive quote into final settlement.
406+
- Contributor compensation is routable only after final settlement and must be
407+
derived from source-to-shares, BTD range slices, contributor roots, and the
408+
settled BTC payment. Estimated depositor earning ranges remain non-final.
409+
- Product surfaces may show quote, observed payment, finality, repair,
410+
refund/escalation, and compensation states, but they must not expose private
411+
settlement payloads, wallet private material, unpaid AssetPack source, raw
412+
protected source, raw prompts, or raw provider responses.
413+
414+
Acceptance for this atom: later V45 specification may refine confirmation
415+
thresholds, wallet-provider adapters, refund mechanisms, and compensation
416+
route mechanics, but it must preserve BTC as settlement money, quote as
417+
source-safe deterministic procurement offer, observed payment as non-final,
418+
confirmed finality as prerequisite to BTD rights transfer, source unlock only
419+
after settlement readback, refund/escalation as fail-closed repair, and
420+
contributor compensation as post-finality source-to-shares allocation.
421+
338422
## Non-goals during V45 opening
339423

340424
- Do not implement V45 gate behavior before the V45 intent discussion is

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@
390390
"check:v45-gate2": "node scripts/check-v45-gate2-knowledge-commoditization-law.mjs",
391391
"check:v45-gate3": "node scripts/check-v45-gate3-assetpack-lifecycle-state-machine.mjs",
392392
"check:v45-gate4": "node scripts/check-v45-gate4-btd-scalar-volume-state-machine.mjs",
393+
"check:v45-gate5": "node scripts/check-v45-gate5-btc-settlement-state-machine.mjs",
393394
"generate:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs",
394395
"check:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs --check",
395396
"check:v38-gate2": "node scripts/check-v38-gate2-inference-surface-inventory.mjs",
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/usr/bin/env node
2+
3+
import { execFileSync } from 'node:child_process';
4+
import { existsSync, readFileSync } from 'node:fs';
5+
import path from 'node:path';
6+
import { fileURLToPath } from 'node:url';
7+
8+
const __filename = fileURLToPath(import.meta.url);
9+
const __dirname = path.dirname(__filename);
10+
const defaultRepoRoot = path.resolve(__dirname, '..');
11+
12+
function read(root, relativePath) {
13+
return readFileSync(path.join(root, relativePath), 'utf8');
14+
}
15+
16+
function exists(root, relativePath) {
17+
return existsSync(path.join(root, relativePath));
18+
}
19+
20+
function git(root, args) {
21+
return execFileSync('git', args, { cwd: root, encoding: 'utf8' }).trim();
22+
}
23+
24+
function assertCheck(failures, condition, message) {
25+
if (!condition) failures.push(message);
26+
}
27+
28+
function normalize(content) {
29+
return content.replace(/\s+/gu, ' ').trim();
30+
}
31+
32+
function parseArgs(argv) {
33+
const args = { repoRoot: defaultRepoRoot, skipBranchCheck: false };
34+
for (let index = 0; index < argv.length; index += 1) {
35+
const arg = argv[index];
36+
if (arg === '--repo-root') args.repoRoot = path.resolve(argv[++index]);
37+
else if (arg === '--skip-branch-check') args.skipBranchCheck = true;
38+
else if (arg === '--help' || arg === '-h') args.help = true;
39+
else throw new Error(`Unknown argument ${arg}`);
40+
}
41+
return args;
42+
}
43+
44+
function printHelp() {
45+
process.stdout.write(
46+
[
47+
'Usage: node scripts/check-v45-gate5-btc-settlement-state-machine.mjs [--skip-branch-check] [--repo-root <path>]',
48+
'',
49+
'Checks the V45 BTC settlement state machine specification atom.',
50+
].join('\n'),
51+
);
52+
process.stdout.write('\n');
53+
}
54+
55+
function main() {
56+
const args = parseArgs(process.argv.slice(2));
57+
if (args.help) {
58+
printHelp();
59+
return;
60+
}
61+
62+
const root = args.repoRoot;
63+
const failures = [];
64+
const pointer = read(root, 'BITCODE_SPEC.txt').trim();
65+
66+
assertCheck(failures, pointer === 'V44', `BITCODE_SPEC.txt must remain V44 during V45 atom work. Observed ${pointer || 'empty'}.`);
67+
68+
if (!args.skipBranchCheck) {
69+
const branch = git(root, ['branch', '--show-current']);
70+
assertCheck(
71+
failures,
72+
branch === 'version/v45' || /^v45\/gate-\d+-[a-z0-9][a-z0-9-]*$/u.test(branch),
73+
`V45 work must occur on version/v45 or v45/gate-N-* branches. Observed ${branch || 'detached HEAD'}.`,
74+
);
75+
}
76+
77+
for (const relativePath of [
78+
'BITCODE_SPEC_V45_NOTES.md',
79+
'BITCODE_SPEC_V44.md',
80+
'packages/btd/src/bitcoin-fees.ts',
81+
'packages/btd/src/btc-fee-operation.ts',
82+
'packages/btd/src/settlement.ts',
83+
'packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts',
84+
'packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts',
85+
'package.json',
86+
'scripts/check-v45-gate4-btd-scalar-volume-state-machine.mjs',
87+
]) {
88+
assertCheck(failures, exists(root, relativePath), `Missing required V45 Gate 5 file: ${relativePath}`);
89+
}
90+
91+
const notes = read(root, 'BITCODE_SPEC_V45_NOTES.md');
92+
const normalizedNotes = normalize(notes);
93+
const packageJson = read(root, 'package.json');
94+
95+
for (const phrase of [
96+
'V45 protocol atom 4: BTC settlement state machine',
97+
'V44 correctly names estimates, quotes, observed payment, final settlement, contributor allocation, delivery, and repair',
98+
'separates BTC fee quotes, PSBT handoff, broadcast observation, finality receipts, settlement unlock, source-to-shares, and compensation statements',
99+
'BTC is Bitcode settlement money and payment truth',
100+
'A BTC quote is a source-safe procurement offer',
101+
'bound to a reviewed Need, selected Fit set, synthesized Need-Fit AssetPack',
102+
'BTD scalar-volume/range',
103+
'A BTC payment is not final merely because a transaction is prepared, signed, broadcast, visible in mempool, or observed by a provider',
104+
'confirmed BTC finality, quote/payment conservation, ledger/database/storage readback, and repair-free settlement receipts',
105+
'btc-not-quoteable',
106+
'btc-quote-issued',
107+
'btc-quote-accepted',
108+
'btc-quote-inactive',
109+
'btc-wallet-ready',
110+
'btc-psbt-prepared',
111+
'btc-psbt-signed',
112+
'btc-broadcast-submitted',
113+
'btc-payment-observed',
114+
'btc-payment-mismatch-repair-required',
115+
'btc-finality-confirmed',
116+
'btc-replaced-reorged-or-failed',
117+
'btc-settlement-finalized',
118+
'btc-contributor-compensation-routable',
119+
'btc-refund-or-escalation-required',
120+
'btc-settlement-repair-required',
121+
'wrong-network, wrong-wallet, or mismatched quotes cannot unlock rights',
122+
'never take server custody of wallet private material',
123+
'Prepared, signed, broadcast, and observed states are not final settlement',
124+
'Payment observation must conserve the accepted quote',
125+
'BTC finality requires the configured confirmation/finality policy',
126+
'Refund or escalation is a repair path, not a successful purchase path',
127+
'Contributor compensation is routable only after final settlement',
128+
'Estimated depositor earning ranges remain non-final',
129+
'must not expose private settlement payloads, wallet private material, unpaid AssetPack source',
130+
'Acceptance for this atom',
131+
'BTC as settlement money',
132+
'quote as source-safe deterministic procurement offer',
133+
'observed payment as non-final',
134+
'confirmed finality as prerequisite to BTD rights transfer',
135+
'source unlock only after settlement readback',
136+
'refund/escalation as fail-closed repair',
137+
'contributor compensation as post-finality source-to-shares allocation',
138+
]) {
139+
assertCheck(failures, normalizedNotes.includes(phrase), `V45 Gate 5 notes must include phrase: ${phrase}`);
140+
}
141+
142+
assertCheck(
143+
failures,
144+
packageJson.includes('"check:v45-gate5": "node scripts/check-v45-gate5-btc-settlement-state-machine.mjs"'),
145+
'package.json must expose check:v45-gate5.',
146+
);
147+
148+
if (failures.length > 0) {
149+
process.stderr.write('V45 Gate 5 BTC settlement state machine check failed:\n');
150+
for (const failure of failures) process.stderr.write(`- ${failure}\n`);
151+
process.exitCode = 1;
152+
return;
153+
}
154+
155+
process.stdout.write('V45 Gate 5 BTC settlement state machine check passed.\n');
156+
}
157+
158+
try {
159+
main();
160+
} catch (error) {
161+
const detail = error instanceof Error ? error.message : String(error);
162+
process.stderr.write(`${detail}\n`);
163+
process.exitCode = 1;
164+
}

0 commit comments

Comments
 (0)