2026-10-14 17:28:33 -07:00
import { test , expect } from '@playwright/test' ;
async function open ( page ) {
const problems = [];
page . on ( 'console' , m => { if ( m . type () === 'error' ) problems . push ( m . text ()); });
page . on ( 'pageerror' , e => problems . push ( String ( e )));
await page . goto ( './' );
await expect ( page . getByRole ( 'heading' , { name : 'Orgchain' })). toBeVisible ();
return problems ;
}
const say = page => page . locator ( '#say' );
const row = ( page , name ) => page . locator ( '#tree-rows .sel' ). filter ({ has : page . locator ( '.l' , { hasText : new RegExp ( `^ ${ name } $` ) }) });
const verb = ( page , name ) => page . locator ( `[data-verb=" ${ name } "]` ). click ();
const submit = ( page , form ) => page . locator ( `#f- ${ form } [type="submit"]` ). click ();
async function found ( page , name ) { await page . locator ( '#found-name' ). fill ( name ); await submit ( page , 'found' ); }
async function birth ( page , name , kind = 'org' ) {
await verb ( page , 'birth' );
await page . locator ( '#birth-name' ). fill ( name );
await page . locator ( `#f-birth input[value=" ${ kind } "]` ). check ();
await submit ( page , 'birth' );
}
2026-10-14 23:16:03 -07:00
async function pay ( page , to , amount ) {
await verb ( page , 'send' );
await page . locator ( '#send-to' ). selectOption ({ label : to });
await page . locator ( '#send-amount' ). fill ( amount );
await submit ( page , 'send' );
}
2026-10-14 17:28:33 -07:00
const slide = ( page , beat ) => page . locator ( '#beat' ). evaluate (( el , v ) => { el . value = v ; el . dispatchEvent ( new Event ( 'input' , { bubbles : true })); }, String ( beat ));
test ( 'found a holding, then birth, award, decide, graduate and close at the default tempo' , async ({ page }) => {
const problems = await open ( page );
await found ( page , 'Acme Holdings' );
await expect ( say ( page )). toHaveText ( 'acme-holdings is founded, at beat 2.' );
await birth ( page , 'studio' );
await expect ( say ( page )). toHaveText ( 'studio is born to acme-holdings, at beat 3.' );
await row ( page , 'studio' ). click ();
2026-10-14 23:16:03 -07:00
await verb ( page , 'birth' );
await expect ( page . locator ( '#act-why' )). toHaveText ( 'studio has no test BTC for gas. acme-holdings can send some.' );
await row ( page , 'acme-holdings' ). click ();
await pay ( page , 'acme-holdings / studio' , '5' );
await expect ( say ( page )). toHaveText ( 'acme-holdings sent 5 tBTC to studio, at beat 4.' );
await row ( page , 'studio' ). click ();
2026-10-14 17:28:33 -07:00
await birth ( page , 'bot' , 'agent' );
2026-10-14 23:16:03 -07:00
await expect ( say ( page )). toHaveText ( 'bot is born to studio, at beat 5.' );
2026-10-14 17:28:33 -07:00
await verb ( page , 'award' );
await page . locator ( '#award-to' ). selectOption ({ label : 'acme-holdings / studio / bot (agent)' });
await page . locator ( '#award-skill' ). fill ( 'launch' );
await page . locator ( '#award-belt' ). selectOption ( 'green' );
await submit ( page , 'award' );
2026-10-14 23:16:03 -07:00
await expect ( say ( page )). toHaveText ( 'bot holds a green belt in launch, at beat 6.' );
2026-10-14 17:28:33 -07:00
await verb ( page , 'decide' );
await page . locator ( '#decide-title' ). fill ( 'Ship weekly' );
await submit ( page , 'decide' );
2026-10-14 23:16:03 -07:00
await expect ( say ( page )). toHaveText ( 'studio decided “Ship weekly”, at beat 7.' );
2026-10-14 17:28:33 -07:00
await verb ( page , 'graduate' );
await page . locator ( '#graduate-go' ). click ();
2026-10-15 00:44:59 -07:00
await expect ( say ( page )). toHaveText ( "studio graduates from acme-holdings, at beat 8. It's a holding of its own now." );
2026-10-14 17:28:33 -07:00
await expect ( row ( page , 'studio' )). toContainText ( 'graduated' );
2026-10-15 00:44:59 -07:00
await expect ( page . locator ( '#holdings-list' )). toContainText ( 'from acme-holdings at beat 8' );
await expect ( row ( page , 'bot' )). toHaveCount ( 0 ); // its seats went with it
2026-10-14 17:28:33 -07:00
await row ( page , 'acme-holdings' ). click ();
await verb ( page , 'close' );
await page . locator ( '#close-go' ). click ();
await expect ( page . locator ( '#close-go' )). toHaveText ( 'Yes, close acme-holdings' );
await page . locator ( '#close-go' ). click ();
2026-10-14 23:16:03 -07:00
await expect ( say ( page )). toHaveText ( 'acme-holdings is closed, at beat 9.' );
2026-10-14 17:28:33 -07:00
await expect ( row ( page , 'acme-holdings' )). toContainText ( 'closed' );
await expect ( row ( page , 'studio' )). not . toContainText ( 'closed' ); // graduated: it holds its own root
await page . locator ( '#timeline-list .ev' , { hasText : 'Ship weekly' }). click ();
await expect ( page . locator ( '#inspect-body' )). toContainText ( '"op": "decide"' );
await expect ( page . locator ( '#inspect-body' )). toContainText ( 'applied' );
2026-10-15 00:44:59 -07:00
await row ( page , 'studio' ). click ();
await page . locator ( '#inspect-body' ). getByRole ( 'button' , { name : 'Open studio' }). click ();
await expect ( page . locator ( '#tree-sub' )). toContainText ( 'studio' );
await expect ( page . locator ( '#timeline-list' )). toContainText ( 'graduate studio from acme-holdings' );
await row ( page , 'bot' ). click ();
await verb ( page , 'birth' );
2026-10-15 12:15:48 -07:00
await expect ( page . locator ( '#act-why' )). toHaveText ( 'Agents give birth only once they graduate.' );
2026-10-14 17:28:33 -07:00
expect ( problems ). toEqual ([]);
});
test ( 'a timed tempo cues acts; Beat now and the clock land them' , async ({ page }) => {
const problems = await open ( page );
await found ( page , 'acme' );
await page . locator ( '[data-tempo="10"]' ). click ();
await birth ( page , 'a' );
await expect ( say ( page )). toHaveText ( 'a is born to acme, cued for the next beat.' );
await birth ( page , 'b' );
await expect ( page . locator ( '#clock-cued' )). toContainText ( '2 cued · next beat in' );
await expect ( row ( page , 'b' )). toContainText ( 'cued' );
await page . locator ( '#beat-now' ). click ();
2026-10-14 22:25:38 -07:00
await expect ( say ( page )). toHaveText ( 'Beat 3: 2 cued acts landed, and acme earned 50 tBTC.' );
2026-10-14 17:28:33 -07:00
await birth ( page , 'c' );
2026-10-14 22:25:38 -07:00
await expect ( say ( page )). toHaveText ( 'Beat 4: 1 cued act landed, and acme earned 50 tBTC.' , { timeout : 15_000 });
2026-10-14 17:28:33 -07:00
await page . locator ( '[data-tempo="manual"]' ). click ();
await birth ( page , 'd' );
await expect ( page . locator ( '#clock-cued' )). toHaveText ( '1 cued · waiting for Beat now' );
await page . locator ( '[data-tempo="each"]' ). click ();
await expect ( page . locator ( '#clock-cued' )). toHaveText ( 'nothing cued' );
await expect ( page . locator ( '#clock-beat' )). toHaveText ( '5' );
expect ( problems ). toEqual ([]);
});
2026-10-14 23:16:03 -07:00
test ( 'beats pay the holdings, every act pays its gas, and test BTC moves across holdings' , async ({ page }) => {
const problems = await open ( page ), inspect = page . locator ( '#inspect-body' ), why = page . locator ( '#act-why' );
2026-10-14 22:25:38 -07:00
await found ( page , 'acme' );
await expect ( row ( page , 'acme' )). toContainText ( '50 tBTC' );
await expect ( page . locator ( '#clock-pays' )). toHaveText ( 'beats pay acme' );
await birth ( page , 'ops' );
await row ( page , 'ops' ). click ();
2026-10-14 23:16:03 -07:00
await verb ( page , 'birth' );
await expect ( why ). toHaveText ( 'ops has no test BTC for gas. acme can send some.' );
2026-10-14 22:25:38 -07:00
await verb ( page , 'send' );
2026-10-14 23:16:03 -07:00
await expect ( why ). toHaveText ( 'ops has no test BTC to send yet. acme can send some.' );
2026-10-14 22:25:38 -07:00
await row ( page , 'acme' ). click ();
await verb ( page , 'send' );
2026-10-14 23:16:03 -07:00
await expect ( page . locator ( '#send-has' )). toHaveText ( '99.999895 tBTC' );
2026-10-14 22:25:38 -07:00
await expect ( page . locator ( '#act-key' )). toHaveText ( "Signs as acme: acme root · m/84'/1'/0'/0/0" );
2026-10-14 23:16:03 -07:00
await expect ( page . locator ( '#act-gas' )). toHaveText ( "Fee: 500 sats, from acme's balance." );
await pay ( page , 'acme / ops' , '12.5' );
await expect ( say ( page )). toHaveText ( 'acme sent 12.5 tBTC to ops, at beat 4.' );
2026-10-14 22:25:38 -07:00
await expect ( row ( page , 'ops' )). toContainText ( '12.5 tBTC' );
await row ( page , 'ops' ). click ();
2026-10-14 23:16:03 -07:00
await verb ( page , 'birth' );
await expect ( page . locator ( '#act-gas' )). toHaveText ( "Gas: 2,500 sats from ops's tank (0 tBTC), which tops up from its balance." );
await birth ( page , 'bot' , 'agent' );
await expect ( say ( page )). toHaveText ( 'bot is born to ops, at beat 5.' );
await expect ( row ( page , 'ops' )). toContainText ( '≈12.5 tBTC' );
2026-10-14 22:25:38 -07:00
await verb ( page , 'send' );
await page . locator ( '#send-to' ). selectOption ({ label : 'acme / ops / bot (agent)' });
await page . locator ( '#send-max' ). click ();
2026-10-14 23:16:03 -07:00
await expect ( page . locator ( '#send-amount' )). toHaveValue ( '12.49989' );
2026-10-14 22:25:38 -07:00
await page . locator ( '#send-amount' ). fill ( '1' );
await submit ( page , 'send' );
await expect ( say ( page )). toHaveText ( 'ops sent 1 tBTC to bot, at beat 6.' );
await expect ( row ( page , 'bot' )). toContainText ( '1 tBTC' );
await page . locator ( '#timeline-list .ev' , { hasText : 'ops → bot' }). click ();
2026-10-14 23:16:03 -07:00
for ( const text of [ '500 sats, retired' , '11.49989 tBTC back to ops' , "ops's SegWit key (BIP-143, SIGHASH_ALL)" ]) await expect ( inspect ). toContainText ( text );
await page . locator ( '#timeline-list .ev' , { hasText : "ops's gas tank" }). click ();
await expect ( inspect ). toContainText ( "ops's gas tank, at its Taproot key" );
2026-10-14 22:25:38 -07:00
await page . locator ( '#beat-now' ). click ();
await expect ( say ( page )). toHaveText ( 'Beat 7: acme earned 50 tBTC.' );
2026-10-14 23:16:03 -07:00
await found ( page , 'beta' );
await expect ( say ( page )). toHaveText ( 'beta is founded, at beat 8.' );
await expect ( page . locator ( '#clock-pays' )). toHaveText ( 'beats pay 2 holdings, split by hashrate' );
await expect ( page . locator ( '#holdings-list' )). toContainText ( '50% of each beat' );
await page . getByRole ( 'button' , { name : "Raise acme's hashrate" }). click ();
await expect ( page . locator ( '#holdings-list' )). toContainText ( '67% of each beat' );
await page . locator ( '#beat-now' ). click ();
await expect ( say ( page )). toHaveText ( 'Beat 9: acme earned ≈33.33 tBTC, beta ≈16.67 tBTC.' );
await page . locator ( '[data-rule="last"]' ). click ();
await expect ( page . locator ( '#clock-pays' )). toHaveText ( 'beats pay the holding that acts' );
await page . locator ( '#beat-now' ). click ();
await expect ( say ( page )). toHaveText ( 'Beat 10: beta earned 50 tBTC.' );
await pay ( page , 'acme / ops' , '2' );
await expect ( say ( page )). toHaveText ( 'beta sent 2 tBTC to acme / ops, at beat 11.' );
await page . locator ( '#holdings-list .hold' , { hasText : 'acme' }). click ();
await expect ( row ( page , 'ops' )). toContainText ( '≈13.5 tBTC' );
await expect ( page . locator ( '#timeline-list' )). toContainText ( '2 tBTC beta → ops' );
2026-10-14 22:25:38 -07:00
await page . locator ( '[data-tempo="manual"]' ). click ();
2026-10-14 23:16:03 -07:00
await pay ( page , 'acme / ops / bot (agent)' , '0.25' );
2026-10-14 22:25:38 -07:00
await expect ( say ( page )). toHaveText ( 'acme sent 0.25 tBTC to bot, cued for the next beat.' );
await expect ( page . locator ( '#clock-cued' )). toHaveText ( '1 cued · waiting for Beat now' );
await page . locator ( '#beat-now' ). click ();
2026-10-14 23:16:03 -07:00
await expect ( say ( page )). toHaveText ( 'Beat 12: 1 cued send landed, and acme earned 50 tBTC.' );
2026-10-14 22:25:38 -07:00
await expect ( row ( page , 'bot' )). toContainText ( '1.25 tBTC' );
2026-10-14 23:16:03 -07:00
await slide ( page , 3 );
2026-10-14 22:25:38 -07:00
await expect ( row ( page , 'ops' )). not . toContainText ( 'tBTC' );
2026-10-14 23:16:03 -07:00
await expect ( row ( page , 'acme' )). toContainText ( '≈100 tBTC' );
2026-10-14 22:25:38 -07:00
await row ( page , 'acme' ). click ();
2026-10-14 23:16:03 -07:00
await expect ( inspect ). toContainText ( 'as of beat 3' );
2026-10-14 22:25:38 -07:00
await page . locator ( '#to-now' ). click ();
expect ( await page . evaluate (() => document . documentElement . scrollWidth <= innerWidth )). toBe ( true );
expect ( problems ). toEqual ([]);
});
2026-10-14 17:28:33 -07:00
test ( 'the sample replays, any beat can be inspected, and the lab survives a reload' , async ({ page }) => {
const problems = await open ( page );
await page . locator ( '#sample' ). click ();
await expect ( say ( page )). toContainText ( 'Loaded the yolopreneur sample' , { timeout : 20_000 });
await expect ( page . locator ( '#tree-rows .sel' )). toHaveCount ( 12 );
await expect ( page . locator ( '#beat-label' )). toHaveText ( 'Now · beat 7' );
await expect ( row ( page , 'lineage' )). toContainText ( 'graduated' );
2026-10-14 22:25:38 -07:00
await expect ( row ( page , 'time' )). toContainText ( '≈38.5 tBTC' );
await expect ( page . locator ( '#timeline-list' )). toContainText ( '40 tBTC yolopreneur → time' );
2026-10-14 17:28:33 -07:00
await expect ( page . locator ( '#sample' )). toBeDisabled ();
await slide ( page , 3 );
await expect ( page . locator ( '#beat-label' )). toHaveText ( 'Beat 3 of 7' );
await expect ( page . locator ( '#tree-rows .sel' )). toHaveCount ( 3 );
await expect ( page . locator ( '#act-past' )). toBeVisible ();
await page . locator ( '#replay' ). click ();
await expect ( page . locator ( '#beat-label' )). toHaveText ( 'Now · beat 7' , { timeout : 15_000 });
await page . locator ( '#timeline-list .ev' , { hasText : 'graduate lineage from well-known' }). click ();
await expect ( page . locator ( '#inspect-body' )). toContainText ( '"op": "graduate"' );
2026-10-14 18:22:22 -07:00
await expect ( page . locator ( '#inspect-body' )). toContainText ( "well-known's key" );
await row ( page , 'lineage' ). click ();
2026-10-14 19:49:05 -07:00
await expect ( page . locator ( '#inspect-body' )). toContainText ( 'lineage root · account 0' );
await expect ( page . locator ( '#inspect-body' )). toContainText ( "BIP-86 at m/86'/1'/0'/0/0" );
2026-10-14 18:22:22 -07:00
await expect ( page . locator ( '#inspect-body' )). toContainText ( 'its own key' );
await row ( page , 'time' ). click ();
2026-10-14 19:49:05 -07:00
await expect ( page . locator ( '#act-key' )). toHaveText ( "Signs as time: yolopreneur root › 1' · m/86'/1'/0'/0/0" );
2026-10-14 19:09:50 -07:00
const inspect = page . locator ( '#inspect-body' );
for ( const text of [ 'ssh-ca' , 'lineage-ca@time.yolopreneur' , 'mkey:' ]) await expect ( inspect ). toContainText ( text );
await row ( page , 'sol' ). click ();
const fact = name => inspect . locator ( '.f' ). filter ({ has : page . locator ( 'dt' , { hasText : new RegExp ( `^ ${ name } $` ) }) });
2026-10-14 19:49:05 -07:00
for ( const [ name , text ] of [[ 'Taproot' , 'bcrt1p' ], [ 'Taproot' , "BIP-86 at m/86'/1'/2'/0/0" ], [ 'SegWit' , 'bcrt1q' ], [ 'SegWit' , "BIP-84 at m/84'/1'/2'/0/0" ],
[ 'npub' , 'npub1' ], [ 'npub' , "NIP-06 at m/44'/1237'/2'/0/0" ], [ 'From' , "yolopreneur root › 1' · account 2" ]]) await expect ( fact ( name )). toContainText ( text );
2026-10-14 19:09:50 -07:00
for ( const text of [ 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5' , 'sol@time.yolopreneur namespaces="git"' , 'sol._domainkey.time.yolopreneur. IN TXT "v=DKIM1; k=ed25519; p=' , 'PublicKey = ' ]) {
2026-10-15 10:48:28 -07:00
await expect ( inspect . locator ( '.code' , { hasText : 'Public lines to paste' }). locator ( 'pre' )). toContainText ( text );
2026-10-14 19:09:50 -07:00
}
2026-10-14 20:14:11 -07:00
await expect ( fact ( 'Tapkey' )). toContainText ( 'bcrt1p' );
await expect ( page . locator ( '#reveal' )). toHaveAttribute ( 'aria-expanded' , 'false' );
await page . locator ( '#reveal' ). click ();
const secrets = inspect . locator ( '.sb' );
for ( const text of [ 'nsec1' , 'BEGIN OPENSSH PRIVATE KEY' , 'PrivateKey = ' , 'account 2 of time' ]) await expect ( secrets ). toContainText ( text );
await expect ( secrets ). toContainText ( /tr\(c[1-9A-HJ-NP-Za-km-z]{51}\)#[a-z0-9]{8}/ );
await secrets . locator ( '.cp' ). first (). click ();
await expect ( secrets . locator ( '.cp' ). first ()). toHaveText ( /Copied|Selected/ );
await row ( page , 'time' ). click ();
await expect ( page . locator ( '#reveal' )). toHaveAttribute ( 'aria-expanded' , 'false' );
await page . locator ( '#reveal' ). click ();
await expect ( secrets ). toContainText ( "Seed · yolopreneur root › 1'" );
await expect ( secrets ). toContainText ( 'BEGIN PRIVATE KEY' );
expect ( await page . evaluate (() => document . documentElement . scrollWidth <= innerWidth )). toBe ( true );
await page . locator ( '#reveal' ). click ();
await expect ( secrets ). toHaveCount ( 0 );
2026-10-14 17:28:33 -07:00
expect ( await page . evaluate (() => document . documentElement . scrollWidth <= innerWidth )). toBe ( true );
await page . reload ();
await expect ( page . locator ( '#tree-rows .sel' )). toHaveCount ( 12 );
await page . locator ( '#theme' ). click ();
await expect ( page . locator ( 'html' )). toHaveAttribute ( 'data-theme' , 'light' );
await page . locator ( '#theme' ). click ();
await page . reload ();
await expect ( page . locator ( 'html' )). toHaveAttribute ( 'data-theme' , 'dark' );
await expect ( page . locator ( '#theme' )). toHaveText ( 'Theme: dark' );
await page . locator ( '#reset' ). click ();
await page . locator ( '#confirm button' , { hasText : 'Erase' }). click ();
await expect ( say ( page )). toHaveText ( 'Erased. This is a fresh lab.' );
await expect ( page . locator ( '#holdings-list li' )). toHaveCount ( 0 );
expect ( problems ). toEqual ([]);
});
2026-10-15 00:32:29 -07:00
test ( 'a node makes an item, another holding bids on it, the holder accepts; on mainnet holdings are funded' , async ({ page }) => {
const problems = await open ( page ), inspect = page . locator ( '#inspect-body' ), why = page . locator ( '#act-why' );
const hold = name => page . locator ( '#holdings-list .hold' , { has : page . locator ( '.hn' , { hasText : new RegExp ( `^ ${ name } $` ) }) });
await found ( page , 'acme' );
await expect ( say ( page )). toHaveText ( 'acme is founded, at beat 2.' );
await found ( page , 'beta' );
await expect ( say ( page )). toHaveText ( 'beta is founded, at beat 3.' );
await verb ( page , 'offer' );
2026-10-15 13:32:29 -07:00
await expect ( why ). toHaveText ( 'Nothing to buy yet. Items come from Make, and an org changes hands from another parent.' );
2026-10-15 00:32:29 -07:00
await hold ( 'acme' ). click ();
await verb ( page , 'make' );
await expect ( page . locator ( '#f-make .hint' )). toContainText ( 'public and permanent' );
await page . locator ( '#make-name' ). fill ( 'Kit' );
await page . locator ( '#make-note' ). fill ( 'A starter kit.' );
await submit ( page , 'make' );
await expect ( say ( page )). toHaveText ( 'acme made kit, at beat 4.' );
await expect ( page . locator ( '#timeline-list' )). toContainText ( 'make kit · acme' );
await expect ( inspect ). toContainText ( 'kit · A starter kit. (made at beat 4)' );
await hold ( 'beta' ). click ();
await verb ( page , 'offer' );
await expect ( page . locator ( '#act-gas' )). toHaveText ( "Fee: 500 sats, from beta's balance, paid only if the holder accepts." );
await page . locator ( '#offer-item' ). selectOption ({ label : 'kit · held by acme' });
await page . locator ( '#offer-price' ). fill ( '2' );
await submit ( page , 'offer' );
await expect ( say ( page )). toHaveText ( 'beta offers 2 tBTC for kit. The offer stands until acme accepts it.' );
await expect ( page . locator ( '#clock-beat' )). toHaveText ( '4' ); // an offer is signatures, not a transaction
await expect ( inspect ). toContainText ( '2 tBTC for kit (open until acme accepts)' );
await hold ( 'acme' ). click ();
await expect ( inspect ). toContainText ( 'beta offers 2 tBTC' );
await inspect . getByRole ( 'button' , { name : "Accept beta's offer for kit" }). click ();
await expect ( say ( page )). toHaveText ( 'acme accepted: kit goes to beta for 2 tBTC, at beat 5.' );
await expect ( inspect ). toContainText ( 'kit · A starter kit. (now held by beta)' );
await page . locator ( '#timeline-list .ev' , { hasText : 'kit acme → beta, for 2 tBTC' }). click ();
for ( const text of [ 'Price' , '2 tBTC' , "beta's SegWit key (SIGHASH_ALL) when it made the offer, then acme's Taproot key when it accepted" ]) {
await expect ( inspect ). toContainText ( text );
}
await page . locator ( '[data-rule="none"]' ). click ();
await expect ( say ( page )). toHaveText ( 'Mainnet: no holding mines. Beats pay the lab wallet, and you fund holdings from it.' );
await expect ( page . locator ( '#clock-pays' )). toHaveText ( 'beats pay the lab wallet' );
await page . locator ( '#fund-to' ). selectOption ({ label : 'beta' });
await page . locator ( '#fund-amount' ). fill ( '3' );
await page . locator ( '#f-fund [type="submit"]' ). click ();
await expect ( say ( page )). toHaveText ( 'The lab wallet funded beta with 3 tBTC, at beat 6.' );
await hold ( 'beta' ). click ();
await expect ( page . locator ( '#timeline-list' )). toContainText ( '3 tBTC lab wallet → beta' );
await expect ( page . locator ( '#holdings-list' )). not . toContainText ( 'of each beat' );
expect ( await page . evaluate (() => document . documentElement . scrollWidth <= innerWidth )). toBe ( true );
expect ( problems ). toEqual ([]);
});
2026-10-15 13:32:29 -07:00
test ( 'the sample ends on both sides of a deal: sol accepts an offer, and lineage takes an ask for an org' , async ({ page }) => {
2026-10-15 00:32:29 -07:00
const problems = await open ( page ), inspect = page . locator ( '#inspect-body' );
await page . locator ( '#sample' ). click ();
await expect ( say ( page )). toContainText ( 'Loaded the yolopreneur sample' , { timeout : 20_000 });
await row ( page , 'lineage' ). click ();
await expect ( inspect ). toContainText ( '0.1 tBTC for launch-kit (open until sol accepts)' );
await row ( page , 'sol' ). click ();
await expect ( inspect ). toContainText ( 'launch-kit · The page, the post and the checklist every venture launches with.' );
await inspect . getByRole ( 'button' , { name : "Accept lineage's offer for launch-kit" }). click ();
await expect ( say ( page )). toHaveText ( 'sol accepted: launch-kit goes to lineage for 0.1 tBTC, at beat 8.' );
await expect ( page . locator ( '#timeline-list' )). toContainText ( 'launch-kit sol → lineage, for 0.1 tBTC' );
await row ( page , 'lineage' ). click ();
await expect ( inspect ). toContainText ( 'launch-kit · The page, the post and the checklist every venture launches with. (made by sol at beat 6)' );
2026-10-15 13:32:29 -07:00
await expect ( row ( page , 'mailr' )). toContainText ( 'for sale' );
await verb ( page , 'offer' );
await page . locator ( '#offer-item' ). selectOption ({ label : 'mailr · an org of well-known · asks 1 tBTC' });
await expect ( page . locator ( '#offer-ask-line' )). toHaveText ( "well-known asks 1 tBTC for mailr. Taking it costs the price, two 500-sat fees and a 600-sat coin of lineage's that leads the deal, and gas to adopt mailr at once." );
await page . locator ( '#offer-take' ). click ();
await expect ( say ( page )). toHaveText ( "lineage bought mailr from well-known for 1 tBTC and adopted it, at beat 9: its keys derive from lineage's seed now." );
await expect ( page . locator ( '#tree-sub' )). toHaveText ( 'lineage · now' );
await expect ( inspect ). toContainText ( 'from well-known to lineage, beat 9' );
await expect ( inspect ). toContainText ( 'derived: lineage holds its seed' );
await expect ( page . locator ( '#timeline-list' )). toContainText ( 'join mailr to lineage, from well-known' );
await page . locator ( '#timeline-list .ev' , { hasText : 'mailr yolopreneur / well-known → lineage, for 1 tBTC' }). click ();
await expect ( inspect ). toContainText ( "the lot's Taproot key (SIGHASH_SINGLE|ANYONECANPAY) when well-known listed it, then lineage's SegWit key (SIGHASH_ALL) when it took it" );
await page . locator ( '#holdings-list .hold' , { has : page . locator ( '.hn' , { hasText : /^yolopreneur$/ }) }). click ();
await expect ( row ( page , 'mailr' )). toContainText ( 'to lineage' );
2026-10-15 00:32:29 -07:00
expect ( await page . evaluate (() => document . documentElement . scrollWidth <= innerWidth )). toBe ( true );
expect ( problems ). toEqual ([]);
});
2026-10-15 00:44:59 -07:00
2026-10-15 13:32:29 -07:00
test ( 'an org sells cash-free: its parent lists it, it is frozen, and another holding takes it and adopts it' , async ({ page }) => {
const problems = await open ( page ), inspect = page . locator ( '#inspect-body' ), why = page . locator ( '#act-why' );
const hold = name => page . locator ( '#holdings-list .hold' , { has : page . locator ( '.hn' , { hasText : new RegExp ( `^ ${ name } $` ) }) });
await found ( page , 'acme' );
await expect ( say ( page )). toHaveText ( 'acme is founded, at beat 2.' );
await found ( page , 'beta' );
await expect ( say ( page )). toHaveText ( 'beta is founded, at beat 3.' );
await hold ( 'acme' ). click ();
await birth ( page , 'ops' );
await expect ( say ( page )). toHaveText ( 'ops is born to acme, at beat 4.' );
await verb ( page , 'sell' );
await page . locator ( '#sell-lot' ). selectOption ({ label : "ops · org 0'" });
await expect ( page . locator ( '#sell-note' )). toContainText ( "Cash-free: ops's money, gas tank and items go home to acme first" );
await expect ( page . locator ( '#act-key' )). toContainText ( 'Signs as ops:' );
await page . locator ( '#sell-price' ). fill ( '3' );
await submit ( page , 'sell' );
await expect ( say ( page )). toHaveText ( "acme lists ops at 3 tBTC: any node can take it. Whatever it held went home to acme first, and it's frozen until the ask is taken or withdrawn." );
await expect ( inspect ). toContainText ( '3 tBTC for ops (open: any node can take it)' );
await row ( page , 'ops' ). click ();
await expect ( inspect ). toContainText ( '3 tBTC, asked by acme' );
await verb ( page , 'decide' );
await expect ( why ). toHaveText ( 'ops is listed for sale at 3 tBTC. Withdraw its ask first.' );
await hold ( 'beta' ). click ();
await verb ( page , 'offer' );
await page . locator ( '#offer-item' ). selectOption ({ label : 'ops · an org of acme · asks 3 tBTC' });
await page . locator ( '#offer-take' ). click ();
await expect ( say ( page )). toHaveText ( "beta bought ops from acme for 3 tBTC and adopted it, at beat 5: its keys derive from beta's seed now." );
await expect ( row ( page , 'ops' )). toBeVisible ();
await expect ( inspect ). toContainText ( 'from acme to beta, beat 5' );
await expect ( inspect ). toContainText ( "beta root › 0'" );
await hold ( 'acme' ). click ();
await expect ( row ( page , 'ops' )). toContainText ( 'to beta' );
await expect ( page . locator ( '#timeline-list' )). toContainText ( 'ops acme → beta, for 3 tBTC' );
expect ( problems ). toEqual ([]);
});
test ( 'an offer on an org: its parent accepts it cash-free, and the buyer adopts it from Inspect' , async ({ page }) => {
const problems = await open ( page ), inspect = page . locator ( '#inspect-body' );
const hold = name => page . locator ( '#holdings-list .hold' , { has : page . locator ( '.hn' , { hasText : new RegExp ( `^ ${ name } $` ) }) });
await found ( page , 'acme' );
await expect ( say ( page )). toHaveText ( 'acme is founded, at beat 2.' );
await found ( page , 'beta' );
await expect ( say ( page )). toHaveText ( 'beta is founded, at beat 3.' );
await hold ( 'acme' ). click ();
await birth ( page , 'ops' );
await expect ( say ( page )). toHaveText ( 'ops is born to acme, at beat 4.' );
await hold ( 'beta' ). click ();
await verb ( page , 'offer' );
await page . locator ( '#offer-item' ). selectOption ({ label : 'ops · an org of acme' });
await expect ( page . locator ( '#offer-ask' )). toBeHidden ();
await page . locator ( '#offer-price' ). fill ( '2' );
await submit ( page , 'offer' );
await expect ( say ( page )). toHaveText ( 'beta offers 2 tBTC for ops. The offer stands until acme accepts it.' );
await hold ( 'acme' ). click ();
await expect ( inspect ). toContainText ( 'ops beta offers 2 tBTC' );
await inspect . getByRole ( 'button' , { name : "Accept beta's offer for ops" }). click ();
await expect ( say ( page )). toHaveText ( 'acme accepted: ops goes to beta for 2 tBTC, at beat 5. Its money and items went home to acme first, and beta adopts it next.' );
await expect ( row ( page , 'ops' )). toContainText ( 'sold' );
await row ( page , 'ops' ). click ();
await expect ( inspect ). toContainText ( 'to beta, which adopts it next' );
await verb ( page , 'decide' );
await expect ( page . locator ( '#act-why' )). toHaveText ( 'ops was sold to beta, which adopts it next.' );
await inspect . getByRole ( 'button' , { name : 'Adopt into beta' }). click ();
await expect ( say ( page )). toHaveText ( "beta adopted ops, at beat 6: its keys derive from beta's seed now." );
await expect ( row ( page , 'ops' )). toContainText ( 'to beta' );
expect ( problems ). toEqual ([]);
});
2026-10-15 00:44:59 -07:00
test ( 'an agent pays a registry in another holding, and the registry awards it a belt' , async ({ page }) => {
const problems = await open ( page );
const hold = name => page . locator ( '#holdings-list .hold' , { has : page . locator ( '.hn' , { hasText : new RegExp ( `^ ${ name } $` ) }) });
await found ( page , 'well-known' );
await expect ( say ( page )). toHaveText ( 'well-known is founded, at beat 2.' );
await found ( page , 'acme' );
await expect ( say ( page )). toHaveText ( 'acme is founded, at beat 3.' );
await birth ( page , 'scout' , 'agent' );
await expect ( say ( page )). toHaveText ( 'scout is born to acme, at beat 4.' );
await pay ( page , 'acme / scout (agent)' , '1' );
await expect ( say ( page )). toHaveText ( 'acme sent 1 tBTC to scout, at beat 5.' );
await row ( page , 'scout' ). click ();
await pay ( page , 'well-known' , '0.1' );
await expect ( say ( page )). toHaveText ( 'scout sent 0.1 tBTC to well-known, at beat 6.' );
await hold ( 'well-known' ). click ();
await verb ( page , 'award' );
await page . locator ( '#award-to' ). selectOption ({ label : 'acme / scout (agent)' });
await page . locator ( '#award-skill' ). fill ( 'listen' );
await submit ( page , 'award' );
await expect ( say ( page )). toHaveText ( 'scout holds a white belt in listen, at beat 7.' );
await expect ( page . locator ( '#timeline-list' )). toContainText ( 'award listen · white → scout, from well-known' );
await hold ( 'acme' ). click ();
await expect ( page . locator ( '#timeline-list' )). toContainText ( 'award listen · white → scout, from well-known' );
await expect ( row ( page , 'scout' )). toContainText ( 'listen' );
expect ( problems ). toEqual ([]);
});
2026-10-15 10:48:28 -07:00
test ( 'a node publishes its keyring; a graduation retires it, and the graduate publishes its new keys' , async ({ page }) => {
const problems = await open ( page ), inspect = page . locator ( '#inspect-body' );
await page . locator ( '#sample' ). click ();
await expect ( say ( page )). toContainText ( 'Loaded the yolopreneur sample' , { timeout : 20_000 });
await row ( page , 'yolopreneur' ). click ();
await expect ( inspect ). toContainText ( 'published at beat 3' );
await expect ( inspect ). toContainText ( 'Its legal-control key, the one that holds the entity NFT' );
await row ( page , 'sol' ). click ();
await expect ( inspect ). toContainText ( 'published at beat 6' );
await expect ( inspect ). toContainText ( "account 2 of its org's seed, the way Ledger Live numbers Ethereum accounts" );
await row ( page , 'lineage' ). click ();
await expect ( inspect ). toContainText ( 'retired at beat 7' );
await expect ( inspect ). toContainText ( 'It graduated, so its keys changed with its new root. Keyring publishes a new one.' );
await verb ( page , 'keyring' );
await expect ( page . locator ( '#keyring-keys' )). toContainText ( 'npub1' );
await submit ( page , 'keyring' );
await expect ( say ( page )). toHaveText ( 'lineage published its keyring, at beat 8: four keys, each signing the same statement.' );
await expect ( inspect ). toContainText ( 'published at beat 8' );
await expect ( inspect . locator ( 'pre' , { hasText : 'lineage/v0 keyring' })). toContainText ( 'ethereum: 0x' );
await expect ( page . locator ( '#act-why' )). toHaveText ( "lineage's keyring already lists these keys, since beat 8." );
expect ( await page . evaluate (() => document . documentElement . scrollWidth <= innerWidth )). toBe ( true );
expect ( problems ). toEqual ([]);
});
2026-10-15 12:15:48 -07:00
test ( 'a holding is a series LLC with its ventures as series, and a seat graduates to go independent' , async ({ page }) => {
const problems = await open ( page ), inspect = page . locator ( '#inspect-body' );
await page . locator ( '#sample' ). click ();
await expect ( say ( page )). toContainText ( 'Loaded the yolopreneur sample' , { timeout : 20_000 });
await expect ( row ( page , 'yolopreneur' )). toContainText ( 'series llc' );
await row ( page , 'lineage' ). click ();
await expect ( inspect ). toContainText ( 'LLC · US-WY · Lineage LLC · via doola' );
await row ( page , 'docscope' ). click ();
await verb ( page , 'charter' );
await expect ( page . locator ( '#charter-forms input[value="series"]' )). toBeChecked ();
await expect ( page . locator ( '#act-key' )). toContainText ( 'Signs as yolopreneur' );
await submit ( page , 'charter' );
await expect ( say ( page )). toHaveText ( 'docscope is a series of yolopreneur, at beat 8.' );
await row ( page , 'yolopreneur' ). click ();
await expect ( inspect . locator ( '.f' , { has : page . locator ( 'dt' , { hasText : /^Series$/ }) })). toContainText ( 'well-known, time, docscope' );
await row ( page , 'sol' ). click ();
await verb ( page , 'graduate' );
await submit ( page , 'graduate' );
await expect ( say ( page )). toContainText ( 'sol graduates from time' );
await expect ( row ( page , 'sol' )). toContainText ( 'graduated' );
await verb ( page , 'charter' );
await page . locator ( '#charter-forms input[value="llc"]' ). check ();
await page . locator ( '#charter-jurisdiction' ). fill ( 'US-DE' );
await page . locator ( '#charter-legal' ). fill ( 'Sol LLC' );
await submit ( page , 'charter' );
await expect ( say ( page )). toContainText ( 'sol is chartered as an LLC in US-DE' );
await verb ( page , 'birth' );
await expect ( page . locator ( '#f-birth' )). toBeVisible ();
expect ( await page . evaluate (() => document . documentElement . scrollWidth <= innerWidth )). toBe ( true );
expect ( problems ). toEqual ([]);
});