Files
orgchain/tests/orgchain.spec.js
T
teahubxyzandClaude Opus 5.5 0af51dc9cd Orgchain v1.3: Taproot and SegWit addresses for every node
Every account now lists standard Bitcoin addresses, so no one has to
derive them: Taproot (BIP-86, m/86'/1'/account'/0/0), which holds the
node's inscription, signs its acts and receives inscriptions, and
SegWit (BIP-84, m/84'/1'/account'/0/0) for payments, beside its npub
(NIP-06). An org is account 0 of its seed and its agents are accounts
1..n, so a wallet that opens an account finds the same addresses. The
lineage key tree page derives the same branches on mainnet paths and
lists the addresses in each agent's keyring.

The v1.2 tapkey (the npub's key, tweaked) is replaced by the standard
BIP-86 key; labs keyed by v1.1 or v1.2 keep their chain keys until
they are reset. Tests pin BIP-86 and BIP-84's own vectors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4upF5LVwmPDDqrhdUfnXh
2026-10-14 19:49:05 -07:00

133 lines
7.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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');
}
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();
await birth(page, 'bot', 'agent');
await expect(say(page)).toHaveText('bot is born to studio, at beat 4.');
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');
await expect(say(page)).toHaveText('bot holds a green belt in launch, at beat 5.');
await verb(page, 'decide');
await page.locator('#decide-title').fill('Ship weekly');
await submit(page, 'decide');
await expect(say(page)).toHaveText('studio decided “Ship weekly”, at beat 6.');
await verb(page, 'graduate');
await page.locator('#graduate-go').click();
await expect(say(page)).toHaveText('studio graduates from acme-holdings, at beat 7. Its edge is adopted now.');
await expect(row(page, 'studio')).toContainText('graduated');
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();
await expect(say(page)).toHaveText('acme-holdings is closed, at beat 8.');
await expect(row(page, 'acme-holdings')).toContainText('closed');
await expect(row(page, 'studio')).not.toContainText('closed'); // graduated: it holds its own root
await row(page, 'bot').click();
await verb(page, 'birth');
await expect(page.locator('#act-why')).toHaveText('Agents do not give birth.');
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');
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();
await expect(say(page)).toHaveText('Beat 3: 2 cued acts landed.');
await birth(page, 'c');
await expect(say(page)).toHaveText('Beat 4: 1 cued act landed.', { timeout: 15_000 });
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([]);
});
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');
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"');
await expect(page.locator('#inspect-body')).toContainText("well-known's key");
await row(page, 'lineage').click();
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");
await expect(page.locator('#inspect-body')).toContainText('its own key');
await row(page, 'time').click();
await expect(page.locator('#act-key')).toHaveText("Signs as time: yolopreneur root › 1' · m/86'/1'/0'/0/0");
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}$`) }) });
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);
for (const text of ['ssh-ed25519 AAAAC3NzaC1lZDI1NTE5', 'sol@time.yolopreneur namespaces="git"', 'sol._domainkey.time.yolopreneur. IN TXT "v=DKIM1; k=ed25519; p=', 'PublicKey = ']) {
await expect(inspect.locator('pre')).toContainText(text);
}
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([]);
});