Orgchain v1.14.1: a belt counts only on its seat's own sat

Placement is now a rule of lineage/v0, not just how Orgchain inscribes:
an award counts only if it is inscribed on its recipient's own sat, so any
indexer can check that a belt is soulbound. The reducer compares sats
(satOf): the lab's sat numbers, ord's, or, without ord's sat index, its
satpoints, which two inscriptions share exactly when they share a sat. ord
events now carry the sat number when ord has one. Belts inscribed anywhere
else, as before v1.14, are rejected.

A promotion is another reinscription on the same sat, over the belt before
it: each badge keeps its skill's earlier belts, and Inspect shows the
ladder ("re-inscribed over white (beat 5)"). The sample's sol now goes
white, then green. An award and its recipient's own close no longer
conflict within a block, since both spend the recipient's inscription.

Rehearsed again on Bitcoin Core regtest from height 178: sixteen beats and
133 transactions; ord shows sol's sat carrying sol, its white belt and its
green belt, in order, each belt charmed reinscription and vindicated, and
the compare matches. The fixture is re-recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4upF5LVwmPDDqrhdUfnXh
This commit is contained in:
teahubxyzandClaude Opus 5.5 committed 2026-10-15 16:10:18 -07:00
1 parent c8c18b073e
commit 5a7f11e49d
17 files changed
+122 -67

No files matched your search

+12
View File
@@ -10,6 +10,7 @@ import { keyOf, scriptHex } from '../src/orgchain/keys.js';
import { askId } from '../src/orgchain/offers.js';
import { BELT_GAS, charmsOf } from '../src/orgchain/belts.js';
import { PROTOCOL } from '../src/orgchain/lineage.js';
import { InscriptionAdapter } from '../src/adapters/inscription.js';
import { Script, Tap, Tx } from '@cmdcode/tapscript';
import { FEE, envelope, tapleafOf } from '../src/adapters/inscription.js';
import { getVin, getVout } from '../src/upstream/primitives.js';
@@ -105,3 +106,14 @@ test('the chain takes a three-input reveal only to inscribe on a held sat: its p
assert.deepEqual([holderCoin(lab.chain, bot).inscriptions.length, satpoint(lab, belt)], [2, satpoint(lab, bot)]);
assert.deepEqual(lineageState(lab).nodes.get(bot).badges.map(b => [b.skill, b.belt]), [['launch', 'white']]);
});
test('a belt inscribed the old way, with postage on a sat of its own, does not count', () => {
const { lab, acme } = twoHoldings(), ops = act(lab, 'birth', acme, { name: 'ops' });
send(lab, acme, ops, SAT);
const bot = act(lab, 'birth', ops, { name: 'bot', kind: 'agent' }), s = lineageState(lab);
const id = new InscriptionAdapter(lab.chain, keyOf(lab.keys, s, s.nodes.get(ops)).wallet)
.inscribe('award launch white', ops, { op: 'award', to: bot, skill: 'launch', belt: 'white' }, { metaprotocol: PROTOCOL });
const after = lineageState(lab), entry = after.log.find(e => e.id === id);
assert.deepEqual([entry.ok, entry.why, after.nodes.get(bot).badges], [false, "a belt counts only on its recipient's own sat", []]);
assert.notEqual(satpoint(lab, id), satpoint(lab, bot));
});
+6 -2
View File
@@ -7,8 +7,12 @@ import { holderOf, keyOf, secretsOf } from '../src/orgchain/keys.js';
import { SAT, payerOf } from '../src/orgchain/money.js';
let n = 0;
const ev = (op, parent = null, meta = {}, height = 1) => ({ id: `${String(++n).padStart(64, '0')}i0`, status: height === null ? 'pending' : 'confirmed',
height, metadata: { op, ...meta }, parent, protocol: PROTOCOL });
// Each inscription on a sat of its own, but a belt on its recipient's, as the chain puts it (SPEC.md, section 7).
const sats = new Map(), ev = (op, parent = null, meta = {}, height = 1) => {
const id = `${String(++n).padStart(64, '0')}i0`, sat = op === 'award' && sats.has(meta.to) ? sats.get(meta.to) : n * 10_000;
sats.set(id, sat);
return { id, sat, status: height === null ? 'pending' : 'confirmed', height, metadata: { op, ...meta }, parent, protocol: PROTOCOL };
};
const charter = (by, meta, height = 2) => ev('charter', by.id, meta, height);
test('a charter records a legal form: a node charters itself, and a series is chartered by its master', () => {
+1 -1
View File
@@ -1 +1 @@
{"from":161,"ord":{"/blockheight":{"status":200,"body":"177"},"/inscriptions/block/161/0":{"status":200,"body":"{\"ids\":[],\"more\":false,\"page_index\":0}"},"/inscriptions/block/162/0":{"status":200,"body":"{\"ids\":[\"92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0\"],\"more\":false,\"page_index\":0}"},"/inscription/92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0":{"status":200,"body":"{\"address\":\"bcrt1p5fpqcc2wh3auczfq9hvyzxq5xj7cz052nedcq6af9x7rrwdmvl6qjnpr0p\",\"charms\":[],\"child_count\":4,\"children\":[\"aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0\",\"c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0\",\"1221a0bbe6de3ecda999d34fcd5d75bdd720f1b697b6c7d94e90c41cd328a585i0\",\"efb276d30bc86f9823b23eea5c66743124be2192ca4e20d65f8d1d8adba7f873i0\"],\"content_length\":17,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":162,\"id\":\"92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0\",\"number\":90,\"parents\":[],\"previous\":\"56ef20f78e26105e4261ad9b5ef634f4a94ad9d591c63c4e13034f35aa158cf0i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"efb276d30bc86f9823b23eea5c66743124be2192ca4e20d65f8d1d8adba7f873:0:0\",\"timestamp\":1792103427,\"value\":1000}"},"/r/metadata/92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0":{"status":200,"body":"\"a2626f7065666f756e64646e616d656b796f6c6f7072656e657572\""},"/inscriptions/block/163/0":{"status":200,"body":"{\"ids\":[\"aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0\",\"c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0\",\"1221a0bbe6de3ecda999d34fcd5d75bdd720f1b697b6c7d94e90c41cd328a585i0\"],\"more\":false,\"page_index\":0}"},"/inscription/aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0":{"status":200,"body":"{\"address\":\"bcrt1pdl8qt5zk5sy3d545s9lx2zcwy4y6s3jj8au3d0nauw5sr8smkccsg03dkr\",\"charms\":[\"vindicated\"],\"child_count\":4,\"children\":[\"1f2f6ec493f7a8f7c6d72441bdacc81f3abb60dcdb35f35c3432fb515a51469ci0\",\"695924409714b890b92372e464b10c3c6d3b476516b55bf6ce4578ec86558680i0\",\"04cac746c154c17a66191d2256130f13b8e55b91e5e9a5161336cc57367fd953i0\",\"ca510a40a8e7c5f1b90ebff2e9064c1a959d9039ae98e56536257ee5f9168884i0\"],\"content_length\":16,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":163,\"id\":\"aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0\",\"number\":91,\"parents\":[\"92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0\"],\"previous\":\"92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"ca510a40a8e7c5f1b90ebff2e9064c1a959d9039ae98e56536257ee5f9168884:1:0\",\"timestamp\":1792103427,\"value\":1000}"},"/r/metadata/aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0":{"status":200,"body":"\"a3626f70656269727468646e616d656a77656c6c2d6b6e6f776e646b696e64636f7267\""},"/inscription/c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0":{"status":200,"body":"{\"address\":\"bcrt1pd7jt377m7d89r83j2f54n4hw55a3ng3vje9xpwmfwe2rq0p9lxns8hpetf\",\"charms\":[\"vindicated\"],\"child_count\":10,\"children\":[\"dc3d8cad57a61fcca86a276689fb3e48626670ab0d1029d0a66053aee26b1706i0\",\"3a29612ca3f8be8e71a70ac74d289b722565d470f943dadb3fbca6acdf6494c8i0\",\"213e2c9a05ad8178eef5e118ec20830faf979c5bee33e1695ac662ca3ab700eai0\",\"896cff83a62fd915d08dd81f10244bc1cd9895c325f2eeb867eda956c00a4ddbi0\"],\"content_length\":10,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":163,\"id\":\"c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"1221a0bbe6de3ecda999d34fcd5d75bdd720f1b697b6c7d94e90c41cd328a585i0\",\"number\":92,\"parents\":[\"92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0\"],\"previous\":\"aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"7226249689af6130a0aaf3713015dac8afcc04f388ff2eeabedb0c1a753af6ac:3:0\",\"timestamp\":1792103427,\"value\":1000}"},"/r/metadata/c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0":{"status":200,"body":"\"a3626f70656269727468646e616d656474696d65646b696e64636f7267\""},"/inscription/1221a0bbe6de3ecda999d34fcd5d75bdd720f1b697b6c7d94e90c4Line truncated
{"from":178,"ord":{"/blockheight":{"status":200,"body":"194"},"/inscriptions/block/178/0":{"status":200,"body":"{\"ids\":[],\"more\":false,\"page_index\":0}"},"/inscriptions/block/179/0":{"status":200,"body":"{\"ids\":[\"da0e1e5969bbfc431692b78bc9b9c95bd0f55a0c0b51db45a4e6cbbe55d1afc2i0\"],\"more\":false,\"page_index\":0}"},"/inscription/da0e1e5969bbfc431692b78bc9b9c95bd0f55a0c0b51db45a4e6cbbe55d1afc2i0":{"status":200,"body":"{\"address\":\"bcrt1pv56wtx9x6zppkvvdrkcp68ne34rmn6v2h4r7eaajh7hgdx4k8gxsdll9wg\",\"charms\":[],\"child_count\":4,\"children\":[\"2d610589d4237edb593652c1ffd2211bf24bae17954acca025fc3396bfa4a35ci0\",\"1b23aad114edfb0c5693332994d994f1ea4892dcc14bd625d8c77706335cc736i0\",\"a655bf88443385d2805b96a6d9a21f2f8eed2a3d44545f26f0898f52824dfac5i0\",\"3512aba39c06f8faf58c24d852a17b91040f7e39e7139cef3c5dda23dffdba4fi0\"],\"content_length\":17,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":179,\"id\":\"da0e1e5969bbfc431692b78bc9b9c95bd0f55a0c0b51db45a4e6cbbe55d1afc2i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"2d610589d4237edb593652c1ffd2211bf24bae17954acca025fc3396bfa4a35ci0\",\"number\":124,\"parents\":[],\"previous\":\"4a00029a6c77b785ad3a0c99bf3176107290ed091285e994989847f9a540a197i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"3512aba39c06f8faf58c24d852a17b91040f7e39e7139cef3c5dda23dffdba4f:0:0\",\"timestamp\":1792105615,\"value\":1000}"},"/r/metadata/da0e1e5969bbfc431692b78bc9b9c95bd0f55a0c0b51db45a4e6cbbe55d1afc2i0":{"status":200,"body":"\"a2626f7065666f756e64646e616d656b796f6c6f7072656e657572\""},"/inscriptions/block/180/0":{"status":200,"body":"{\"ids\":[\"2d610589d4237edb593652c1ffd2211bf24bae17954acca025fc3396bfa4a35ci0\",\"1b23aad114edfb0c5693332994d994f1ea4892dcc14bd625d8c77706335cc736i0\",\"a655bf88443385d2805b96a6d9a21f2f8eed2a3d44545f26f0898f52824dfac5i0\"],\"more\":false,\"page_index\":0}"},"/inscription/2d610589d4237edb593652c1ffd2211bf24bae17954acca025fc3396bfa4a35ci0":{"status":200,"body":"{\"address\":\"bcrt1pvt0w7l9yakudpzkfn8f2s8d73t4rn6vvmhzhcsgnkv5fva4a9lwqcur07s\",\"charms\":[\"vindicated\"],\"child_count\":4,\"children\":[\"5705848da843ed28dae86c6c6166ba5d3a83ec3b4ea41373206b31cc0cecd9d6i0\",\"b3a59ac312b0a02ccb7e75d12c3f5ed8653e5835124fe6f1c3f44556a6d908fci0\",\"8651d1373e26b782d61d673131ef5dd3925a4bd62f0fc6845de0d404680f03bbi0\",\"c9394aa6483606ef5df18b4a878dfd98db6a671c5986a5bfb3527364b93fea10i0\"],\"content_length\":16,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":180,\"id\":\"2d610589d4237edb593652c1ffd2211bf24bae17954acca025fc3396bfa4a35ci0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"1b23aad114edfb0c5693332994d994f1ea4892dcc14bd625d8c77706335cc736i0\",\"number\":125,\"parents\":[\"da0e1e5969bbfc431692b78bc9b9c95bd0f55a0c0b51db45a4e6cbbe55d1afc2i0\"],\"previous\":\"da0e1e5969bbfc431692b78bc9b9c95bd0f55a0c0b51db45a4e6cbbe55d1afc2i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"c9394aa6483606ef5df18b4a878dfd98db6a671c5986a5bfb3527364b93fea10:1:0\",\"timestamp\":1792105615,\"value\":1000}"},"/r/metadata/2d610589d4237edb593652c1ffd2211bf24bae17954acca025fc3396bfa4a35ci0":{"status":200,"body":"\"a3626f70656269727468646e616d656a77656c6c2d6b6e6f776e646b696e64636f7267\""},"/inscription/1b23aad114edfb0c5693332994d994f1ea4892dcc14bd625d8c77706335cc736i0":{"status":200,"body":"{\"address\":\"bcrt1ppsdeg57f0txdq86l6twwrkxtrnzhrzzpuug32mlugta8rxv6rc6quqtly7\",\"charms\":[\"vindicated\"],\"child_count\":11,\"children\":[\"63d53594cdc98a9ce14f6ab396232bc57d27b8e9d1b7ed92d461f45c661f193ei0\",\"987fc9c2a9bb00748865b89ff3b02bf932b2cbd5878467440b91c2ef70070e3ei0\",\"533c0f252994efcc6c5fbc5ff7e8c5da55ca92885cc8c3c12710509cabda2d67i0\",\"336dc98ff84ff0ce2759af75ce8bcc6a8440cf3080339d5c9f4f41c9fd92b187i0\"],\"content_length\":10,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":180,\"id\":\"1b23aad114edfb0c5693332994d994f1ea4892dcc14bd625d8c77706335cc736i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"a655bf88443385d2805b96a6d9a21f2f8eed2a3d44545f26f0898f52824dfac5i0\",\"number\":126,\"parents\":[\"da0e1e5969bbfc431692b78bc9b9c95bd0f55a0c0b51db45a4e6cbbe55d1afc2i0\"],\"previous\":\"2d610589d4237edb593652c1ffd2211bf24bae17954acca025fc3396bfa4a35ci0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"d46da625c6f7109a7b354f894af10c9a0f75849ad1e4b0201e91f601e74b0f2e:3:0\",\"timestamp\":1792105615,\"value\":1000}"},"/r/metadata/1b23aad114edfb0c5693332994d994f1ea4892dcc14bd625d8c77706335cc736i0":{"status":200,"body":"\"a3626f70656269727468646e616d656474696d65646b696e64636f7267\""},"/inscription/a655bf88443385d2805b96a6d9a21f2f8eed2a3d44545f26f08Line truncated
+13 -10
View File
@@ -20,7 +20,7 @@ const hex = script => Script.encode(script, false).hex;
test('real ord indexed the regtest rehearsal, and the same reducer folds it into the trees the lab drew', async () => {
const events = await ordEvents(ordClient('http://ord.test', replay), { from: fixture.from });
assert.equal(events.length, 34);
assert.equal(events.length, 35);
assert.ok(events.every(e => e.protocol === 'lineage/v0' && e.parents <= 1 && /^[0-9a-f]{64}:\d+$/.test(e.location)));
const s = ordState(events), id = name => [...s.nodes.values()].find(n => n.name === name).id;
assert.ok(s.log.every(e => e.ok));
@@ -46,17 +46,20 @@ test('real ord indexed the regtest rehearsal, and the same reducer folds it into
assert.deepEqual([...s.nodes.values()].filter(n => n.keyring).map(n => [n.name, n.keyring.retired?.cause ?? null]).sort(),
[['lineage', null], ['scout', 'move'], ['sol', 'move'], ['yolopreneur', null]]);
assert.equal(s.bound.size, 8);
// Byte for byte: the same events in the same beats, the same metadata and parents, every inscription in the same
// output (the launch kit where scout's take of lineage's ask put it, and with scout's tree split back at lineage's
// keys). Only the miner's order within blocks differs, a join and its adoption included.
// Byte for byte: the same events in the same beats, the same metadata and parents, every inscription on the same sat
// (the launch kit where scout's take of lineage's ask put it, and with scout's tree split back at lineage's keys).
// Only the miner's order within blocks differs, a join and its adoption included.
const { problems, reordered } = compareWithLab({ chain: new LocalChain(fixture.chain) }, events);
assert.deepEqual(problems, []);
assert.equal(reordered, 15);
// Belts ride their seats' own sats: on ord, each award shares its recipient's satpoint, through both trees' sales and
// their splits, and is marked ♻️ reinscription (and ❤️‍🔥 vindicated: it was inscribed from the reveal's third input).
const on = new Map(events.map(e => [e.id, e]));
assert.deepEqual(s.log.filter(e => e.op === 'award').map(e => [s.nodes.get(e.meta.to).name, on.get(e.id).satpoint === on.get(e.meta.to).satpoint, on.get(e.id).charms]),
[['sol', true, ['reinscription', 'vindicated']], ['luna', true, ['reinscription', 'vindicated']], ['scout', true, ['reinscription', 'vindicated']]]);
assert.equal(reordered, 17);
// Belts ride their seats' own sats, or they wouldn't count: on ord, each award shares its recipient's satpoint, through
// both trees' sales and their splits, and is marked ♻️ reinscription (and ❤️‍🔥 vindicated: it was inscribed from the
// reveal's third input). Sol's launch belt went white, then green: two reinscriptions on its sat, and the record keeps
// the ladder.
const on = new Map(events.map(e => [e.id, e])), charmed = ['reinscription', 'vindicated'];
assert.deepEqual(s.log.filter(e => e.op === 'award').map(e => [s.nodes.get(e.meta.to).name, e.meta.belt, on.get(e.id).satpoint === on.get(e.meta.to).satpoint, on.get(e.id).charms]),
[['sol', 'white', true, charmed], ['sol', 'green', true, charmed], ['luna', 'white', true, charmed], ['scout', 'white', true, charmed]]);
assert.deepEqual(s.nodes.get(id('sol')).badges.map(b => [b.skill, b.belt, b.earlier.map(x => x.belt)]), [['launch', 'green', ['white']]]);
});
test('a reveal commits to its exact script (BIP-341), and the lab still loads reveals made the old way', () => {
+1
View File
@@ -89,6 +89,7 @@ test('the yolopreneur sample is six beats of the drawing in docs/lineage.md', ()
assert.equal(named('lineage').edge, 'adopted');
assert.deepEqual(s.roots.map(r => r.name), ['yolopreneur', 'lineage']); // graduated: a holding of its own
assert.deepEqual(named('sol').badges.map(b => `${b.skill}:${b.belt}`), ['launch:green']);
assert.deepEqual(named('sol').badges[0].earlier.map(x => [x.belt, x.beat]), [['white', 5]]); // re-inscribed over it, on sol's sat
// Three keyrings; lineage's retires when it graduates, since its keys change with its root.
assert.deepEqual(['yolopreneur', 'sol', 'lineage'].map(x => [named(x).keyring.beat, named(x).keyring.retired?.cause ?? null]),
[[3, null], [6, null], [6, 'graduate']]);
+19 -2
View File
@@ -3,8 +3,12 @@ import assert from 'node:assert/strict';
import { PROTOCOL, census, reduce, rows, slug, summary, trail } from '../src/orgchain/lineage.js';
let n = 0;
const ev = (op, parent = null, meta = {}, height = 1) => ({ id: `${String(++n).padStart(64, '0')}i0`, status: height === null ? 'pending' : 'confirmed',
height, metadata: { op, ...meta }, parent, protocol: PROTOCOL });
// Each inscription on a sat of its own, but a belt on its recipient's, as the chain puts it (SPEC.md, section 7).
const sats = new Map(), ev = (op, parent = null, meta = {}, height = 1) => {
const id = `${String(++n).padStart(64, '0')}i0`, sat = op === 'award' && sats.has(meta.to) ? sats.get(meta.to) : n * 10_000;
sats.set(id, sat);
return { id, sat, status: height === null ? 'pending' : 'confirmed', height, metadata: { op, ...meta }, parent, protocol: PROTOCOL };
};
const draw = (s, root) => rows(s, root).map(r => r.guide + (r.type === 'group' ? r.label : `${r.path} ${r.node.name}`));
test('found and birth build the tree; orgs and agents are indexed separately', () => {
@@ -81,6 +85,19 @@ test('a belt can cross holdings: the event shows in both, and the issuer acted',
assert.equal(summary(entry, s), 'award listen · white → bot, from well-known');
});
test("a belt counts only on its recipient's own sat, and a newer one in its skill is re-inscribed over the ones before", () => {
const root = ev('found', null, { name: 'acme' }), bot = ev('birth', root.id, { name: 'bot', kind: 'agent' });
const white = ev('award', root.id, { to: bot.id, skill: 'launch', belt: 'white' }), green = ev('award', root.id, { to: bot.id, skill: 'launch', belt: 'green' }, 2);
const stray = { ...ev('award', root.id, { to: bot.id, skill: 'post', belt: 'white' }, 2), sat: 7 }; // on a sat of its own
const s = reduce([root, bot, white, green, stray]), [b] = s.nodes.get(bot.id).badges;
assert.equal(s.log.find(e => e.id === stray.id).why, "a belt counts only on its recipient's own sat");
assert.deepEqual([s.nodes.get(bot.id).badges.length, b.belt, b.earlier.map(x => [x.belt, x.beat, x.id])], [1, 'green', [['white', 1, white.id]]]);
// Without a sat index, ord's satpoints serve: two inscriptions share one exactly when they share a sat.
const at = (e, satpoint) => ({ ...e, sat: null, satpoint });
const t = reduce([at(root, `${'a'.repeat(64)}:0:0`), at(bot, `${'b'.repeat(64)}:0:0`), at(white, `${'b'.repeat(64)}:0:0`), at(stray, `${'b'.repeat(64)}:0:1`)]);
assert.deepEqual(t.log.map(e => e.ok), [true, true, true, false]);
});
test('upTo shows a past beat; pending adds cued events', () => {
const root = ev('found', null, { name: 'acme' }, 1), a = ev('birth', root.id, { name: 'a' }, 2), b = ev('birth', root.id, { name: 'b' }, null),
award = ev('award', root.id, { to: a.id, skill: 'post', belt: 'white' }, null);
+1
View File
@@ -204,6 +204,7 @@ test('the sample replays, any beat can be inspected, and the lab survives a relo
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();
await expect(inspect).toContainText("from time, beat 6 · ♻️ on sol's own sat, re-inscribed over white (beat 5)"); // one sat, its whole ladder
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);