Files
teahubxyzandClaude Opus 5.5 5a7f11e49d 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
2026-10-15 16:10:18 -07:00

120 lines
9.2 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { PROTOCOL, census, reduce, rows, slug, summary, trail } from '../src/orgchain/lineage.js';
let n = 0;
// 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', () => {
const root = ev('found', null, { name: 'yolopreneur' }), wk = ev('birth', root.id, { name: 'well-known' }, 2),
time = ev('birth', root.id, { name: 'time', kind: 'org' }, 2), sol = ev('birth', time.id, { name: 'sol', kind: 'agent' }, 3),
doc = ev('birth', time.id, { name: 'docscope' }, 3);
const s = reduce([root, wk, time, sol, doc]);
assert.ok(s.log.every(e => e.ok));
assert.deepEqual([time, sol, doc].map(e => s.nodes.get(e.id).index), [1, 0, 0]);
assert.equal(s.nodes.get(sol.id).edge, 'derived');
assert.equal(s.nodes.get(root.id).kind, 'holding');
assert.deepEqual(draw(s, root.id), ['● yolopreneur', '└─ orgs', " ├─ 0' well-known", " └─ 1' time",
' ├─ agents', " │ └─ 0' sol", ' └─ orgs', " └─ 0' docscope"]);
assert.equal(trail(s, sol.id), 'yolopreneur / time / sol');
assert.deepEqual(census(s, root.id), { orgs: 3, agents: 1, belts: 0, decisions: 0, closed: 0 });
assert.equal(summary(s.log[3], s), 'birth sol (agent) ← time');
});
test('every rule rejects with a reason, and rejected events change nothing', () => {
const root = ev('found', null, { name: 'acme' }), bot = ev('birth', root.id, { name: 'bot', kind: 'agent' }),
ops = ev('birth', root.id, { name: 'ops' }), other = ev('found', null, { name: 'other' });
const bad = [
[ev('found', root.id, { name: 'x' }), /no parent/], [ev('found', null, { name: 'Acme Co' }), /lowercase/],
[ev('found', null, { name: 'acme' }), /already named acme/], [ev('birth', 'f'.repeat(64) + 'i0', { name: 'x' }), /not in this lineage/],
[ev('birth', bot.id, { name: 'x' }), /agents give birth only once they graduate/], [ev('birth', root.id, { name: 'ops' }), /already has an active ops/],
[ev('award', bot.id, { to: ops.id, skill: 'launch', belt: 'green' }), /agents award belts only once they graduate/],
[ev('award', root.id, { to: 'f'.repeat(64) + 'i0', skill: 'launch', belt: 'green' }), /another node in this lineage/],
[ev('award', root.id, { to: root.id, skill: 'launch', belt: 'green' }), /another node/],
[ev('award', root.id, { to: bot.id, skill: 'launch', belt: 'gold' }), /belt/], [ev('decide', root.id, { title: ' ' }), /title/],
[ev('graduate', ops.id, { of: bot.id }), /only its parent/], [ev('graduate', root.id, { of: root.id }), /only its parent/],
[ev('close'), /spends the node/], [ev('nope', root.id), /not a lineage\/v0 op/],
];
const s = reduce([root, bot, ops, other, ...bad.map(([e]) => e)]);
bad.forEach(([e, why]) => { const entry = s.log.find(x => x.id === e.id); assert.equal(entry.ok, false, e.metadata.op); assert.match(entry.why, why); });
assert.equal(s.nodes.size, 4);
assert.match(summary(s.log.find(x => x.id === bad[4][0].id), s), /^birth: agents give birth only once they graduate$/);
});
test('graduation flips the edge; closing cascades through derived children only', () => {
const root = ev('found', null, { name: 'acme' }), lab = ev('birth', root.id, { name: 'lab' }), kid = ev('birth', lab.id, { name: 'kid' }),
seat = ev('birth', kid.id, { name: 'seat', kind: 'agent' }), grad = ev('birth', lab.id, { name: 'grad' }),
up = ev('graduate', lab.id, { of: grad.id }, 2), again = ev('graduate', lab.id, { of: grad.id }, 2),
award = ev('award', lab.id, { to: seat.id, skill: 'launch', belt: 'green' }, 2), close = ev('close', lab.id, {}, 3),
after = ev('birth', lab.id, { name: 'late' }, 4), still = ev('decide', grad.id, { title: 'Carry on', why: 'adopted' }, 4);
const s = reduce([root, lab, kid, seat, grad, up, again, award, close, after, still]);
assert.equal(s.nodes.get(grad.id).edge, 'adopted');
assert.equal(s.nodes.get(grad.id).graduated, 2);
assert.match(s.log.find(e => e.id === again.id).why, /already graduated/);
assert.deepEqual(s.nodes.get(seat.id).badges.map(b => `${b.skill}:${b.belt}`), ['launch:green']);
assert.deepEqual([lab, kid, seat, grad].map(e => s.nodes.get(e.id).status), ['closed', 'closed', 'closed', 'active']);
assert.deepEqual(s.log.find(e => e.id === close.id).also, [kid.id, seat.id]);
assert.equal(summary(s.log.find(e => e.id === close.id), s), 'close lab and 2 derived');
assert.match(s.log.find(e => e.id === after.id).why, /parent is closed/);
assert.equal(s.nodes.get(grad.id).decisions[0].title, 'Carry on');
// A graduate is a holding of its own: its parent's tree keeps a row for it, and its own tree starts at it.
assert.deepEqual(s.roots.map(r => r.id), [root.id, grad.id]);
assert.equal(s.nodes.get(grad.id).root, grad.id);
assert.deepEqual(s.log.find(e => e.id === up.id).roots, [root.id, grad.id]);
assert.deepEqual(s.log.find(e => e.id === still.id).roots, [grad.id]);
assert.deepEqual(rows(s, root.id).filter(r => r.away).map(r => r.node.name), ['grad']);
assert.deepEqual(draw(s, grad.id), ['● grad']);
assert.equal(trail(s, grad.id), 'grad');
assert.deepEqual(census(s, root.id), { orgs: 2, agents: 1, belts: 1, decisions: 0, closed: 3 });
assert.deepEqual(census(s, grad.id), { orgs: 0, agents: 0, belts: 0, decisions: 1, closed: 0 });
});
test('a belt can cross holdings: the event shows in both, and the issuer acted', () => {
const wk = ev('found', null, { name: 'well-known' }), other = ev('found', null, { name: 'acme' }), bot = ev('birth', other.id, { name: 'bot', kind: 'agent' });
const award = ev('award', wk.id, { to: bot.id, skill: 'listen', belt: 'white' }, 2);
const s = reduce([wk, other, bot, award]), entry = s.log.at(-1);
assert.equal(entry.ok, true);
assert.deepEqual([entry.root, entry.roots], [wk.id, [wk.id, other.id]]);
assert.deepEqual(s.nodes.get(bot.id).badges.map(b => [b.skill, b.belt, b.by]), [['listen', 'white', wk.id]]);
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);
assert.equal(reduce([root, a, b, award], { upTo: 1 }).nodes.size, 1);
assert.equal(reduce([root, a, b, award]).nodes.size, 2);
const live = reduce([root, a, b, award], { pending: true });
assert.equal(live.nodes.get(b.id).born, 'cued');
assert.equal(live.nodes.get(a.id).badges[0].beat, 'cued');
assert.equal(live.log.filter(e => e.pending).length, 2);
});
test('summaries and slugs', () => {
const root = ev('found', null, { name: 'acme' }), ops = ev('birth', root.id, { name: 'ops' }), seat = ev('birth', root.id, { name: 'sol', kind: 'agent' });
const more = [ev('award', root.id, { to: seat.id, skill: 'launch', belt: 'green' }), ev('decide', ops.id, { title: 'Ship weekly' }), ev('graduate', root.id, { of: ops.id })];
const s = reduce([root, ops, seat, ...more]);
assert.deepEqual(s.log.map(e => summary(e, s)), ['found acme', 'birth ops ← acme', 'birth sol (agent) ← acme',
'award launch · green → sol, from acme', 'decide “Ship weekly” · ops', 'graduate ops from acme']);
assert.deepEqual(['Acme Holdings LLC', ' --Zoë_lab-- ', 'a'.repeat(50), '!!!'].map(slug), ['acme-holdings-llc', 'zoe-lab', 'a'.repeat(40), '']);
});