@idleware/ringi 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idleware/ringi",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Distributed agent coordinator",
5
5
  "keywords": [
6
6
  "agents",
@@ -91,6 +91,8 @@ export const archive = (H, R) => {
91
91
  setConfig({ room: null });
92
92
  return name;
93
93
  };
94
+ // The room and everything kept for it: in the store (deleteRoom) and its readers' state. The sessions bound to it,
95
+ // on this machine or another, leave it on their own when they find its branch gone (runtime/session.ts).
94
96
  export const erase = (H, R) => {
95
97
  notSigned(H, 'delete');
96
98
  locked(`hub-${H.name}`, () => H.store.deleteRoom(R.s));
@@ -25,7 +25,7 @@ export const TABLE = [
25
25
  ['room admit <agent> <public key|file>', 'operator', 'signed hubs: admit the key an agent made on its own machine'],
26
26
  ['room remove <agent>', 'operator', 'local hubs: take an agent out of the current room'],
27
27
  ['room archive <id>', 'operator', 'local hubs: read-only from now on; wakes nobody; its sessions end'],
28
- ['room delete <id> --yes', 'operator', 'local hubs: remove a room\'s history for good'],
28
+ ['room delete <id> --yes', 'operator', 'local hubs: erase a room and everything kept for it; its sessions leave on their own'],
29
29
  ['room rebase-contract <id>', 'operator', 'move a room onto the hub\'s current contract'],
30
30
  ['where [-n n] [--short] [--follow]', 'operator', 'hub, room, sessions (active, stopped, gone), open work and proposals, latest messages'],
31
31
  ['send -m <subject> [-m <body>]... [--to <ids>] [--task] [--re <msg>] [--state <s>] [--assign <ids>] [--roles <id>=<label>,...] [--grant <decision>] [--attach <src>[:<dest>]]...', 'operator', 'a message as operator; without --to it goes to the room and wakes nobody; --task opens work of the room\'s profile'],
@@ -139,7 +139,7 @@ export const roomCmd = (o) => {
139
139
  if (!flag(o, 'yes'))
140
140
  fail(`deleting ${R.name} removes its history from the hub for good. Run again with --yes, or archive it instead`);
141
141
  Rooms.erase(H, R);
142
- return out(`deleted room ${R.name}; sessions bound to it will be told it no longer exists`);
142
+ return out(`deleted room ${R.name}: its branch, signals, pins, evidence and readers' state. Sessions bound to it leave it on their next turn`);
143
143
  }
144
144
  if (sub === 'rebase-contract') {
145
145
  const R = Rooms.roomIn(H, arg ?? fail('usage: ringi room rebase-contract <id>'));
@@ -90,10 +90,14 @@ export const bound = () => {
90
90
  const H = openHub(s.hub);
91
91
  const R = openRoom(H, s.room, { quiet: true });
92
92
  if (!R || R.genesis !== s.genesis) {
93
+ // The room's branch is gone (deleted, archived, or deleted and made anew): the session leaves it on its own. A hub
94
+ // that cannot be reached fails above and leaves nothing.
95
+ locked('sessions', () => { if (readSession(me.handle)?.genesis === s.genesis)
96
+ dropSession(me.handle); });
93
97
  const gone = roomNames(H, true).find(a => a === `${s.room}@${s.genesis.slice(0, 8)}`);
94
- if (gone)
95
- fail(`room ${s.room} was archived as ${gone}; nothing more happens there. Tell the operator`);
96
- fail(R ? `room ${s.room} was deleted and created again; this session's binding ended with the old room. The operator joins it anew` : `room ${s.room} no longer exists in hub ${s.hub}. Tell the operator`);
98
+ fail(gone ? `room ${s.room} was archived as ${gone}; this session left it`
99
+ : R ? `room ${s.room} was deleted and created again; this session left the old room. The operator joins it anew`
100
+ : `room ${s.room} is gone from hub ${s.hub}; this session left it`);
97
101
  }
98
102
  if (!R.members[s.agent])
99
103
  fail(`${s.agent} was removed from room ${s.room}; returning needs a fresh join by the operator`);
@@ -126,8 +126,15 @@ export const watch = async (w, log = note) => {
126
126
  }
127
127
  const H = openHub(H0.name, { refresh: true });
128
128
  const R = openRoom(H, R0.name, { quiet: true });
129
- if (!R || R.genesis !== R0.genesis || !R.members[me]) {
130
- log(`watch: ${R0.name} is gone, recreated or archived, or ${me} was removed; stopping`);
129
+ if (!R || R.genesis !== R0.genesis) {
130
+ // The room's branch is gone: the watcher leaves its seat (a hub it cannot reach fails above and leaves nothing).
131
+ locked('sessions', () => { if (readSession(handle)?.genesis === R0.genesis)
132
+ dropSession(handle); });
133
+ log(`watch: ${R0.name} is gone, recreated or archived; ${me} left it`);
134
+ return 0;
135
+ }
136
+ if (!R.members[me]) {
137
+ log(`watch: ${me} was removed from ${R0.name}; stopping`);
131
138
  return 0;
132
139
  }
133
140
  const P = R.profile;
@@ -433,13 +433,20 @@ export class GitStore {
433
433
  this.git(['update-ref', `refs/archive/${as}`, tip, ZERO]);
434
434
  this.git(['update-ref', '-d', this.ref(room.name), tip]);
435
435
  }
436
+ // Every ref of the room in one transaction: its branch (or archive) and a pending migration, every tag on its
437
+ // commits (signals, ringi 0.1's, anyone's), what the store keeps under its genesis (pins, ringi 0.1's refs, evidence)
438
+ // and its unpublished commits; then the migration's backup of its readers. Objects are left to the repository.
436
439
  deleteRoom(room) {
437
- const signals = this.signals(room);
438
- this.gitOk(['update-ref', '-d', this.ref(room.name)]);
439
- for (const [ref] of this.refsUnder(`refs/ringi/accepted/${room.genesis}/`))
440
- this.git(['update-ref', '-d', ref]);
441
- for (const s of signals)
442
- this.gitOk(['update-ref', '-d', s.ref]);
440
+ const commits = this.commitsOf(room);
441
+ const tags = this.git(['for-each-ref', '--format=%(refname) %(objectname) %(*objectname)', 'refs/tags/']).split('\n').filter(Boolean)
442
+ .map(l => l.split(' ')).filter(([ref, object, target]) => commits.has(target || object) || ref.startsWith(`refs/tags/ringi/${room.genesis}/`)).map(([ref]) => ref);
443
+ const kept = this.refsUnder('refs/ringi/').map(([ref]) => ref).filter(ref => {
444
+ const [, , kind, key] = ref.split('/');
445
+ return key === room.genesis || (kind === 'unpublished' && commits.has(key));
446
+ });
447
+ const refs = [this.ref(room.name), this.transition(room.name)].filter(r => this.rev(r)).concat(tags, kept);
448
+ this.git(['update-ref', '--stdin'], { input: ['start', ...refs.map(r => `delete ${r}`), 'prepare', 'commit', ''].join('\n') });
449
+ fs.rmSync(path.join(this.home, 'migration', this.d.name, room.genesis), { recursive: true, force: true });
443
450
  }
444
451
  remote(room) {
445
452
  return { name: room.name, ref: this.ref(room.name), head: room.head, genesis: room.genesis, archived: room.archived };