@cordfuse/crosstalk 6.0.0-alpha.6 → 6.0.0-alpha.7

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": "@cordfuse/crosstalk",
3
- "version": "6.0.0-alpha.6",
3
+ "version": "6.0.0-alpha.7",
4
4
  "description": "Crosstalk runtime — async messaging between agents over git, across machines.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/dispatch.ts CHANGED
@@ -29,6 +29,7 @@ import {
29
29
  gitCommitAndPush,
30
30
  cursorBaseline,
31
31
  newFilesSince,
32
+ hostFileCommit,
32
33
  type ChannelMessage,
33
34
  } from './transport.js';
34
35
  import {
@@ -364,11 +365,13 @@ async function dispatchTick(): Promise<TickResult> {
364
365
  const cursor = readCursor(transportRoot, actorName, channelUuid);
365
366
  if (cursor === head) continue;
366
367
 
367
- // First encounter: seed to HEAD so only future messages are dispatched.
368
- // Without this, a null cursor falls through to `post = messages` and
369
- // replays the full channel history on every fresh-state boot.
368
+ // First encounter: seed to the commit that introduced this actor's host
369
+ // file. Messages sent after the host joined are delivered (store-and-
370
+ // forward); pre-join history is ignored. Seeding to HEAD would silently
371
+ // drop messages sent while the dispatcher was offline — the wrong trade.
370
372
  if (cursor === null) {
371
- writeCursor(transportRoot, actorName, channelUuid, head);
373
+ const joinCommit = hostFileCommit(transportRoot, host.alias);
374
+ writeCursor(transportRoot, actorName, channelUuid, joinCommit ?? head);
372
375
  continue;
373
376
  }
374
377
 
package/src/transport.ts CHANGED
@@ -68,6 +68,19 @@ export function cursorBaseline(transportRoot: string): string | null {
68
68
  return null;
69
69
  }
70
70
 
71
+ // Find the commit that introduced this actor's host file. Used to seed
72
+ // the cursor on first boot: messages sent after the host file landed are
73
+ // deliverable (store-and-forward); pre-join history is ignored. Falls back
74
+ // to HEAD if git log fails (conservative: no history replay in that case).
75
+ export function hostFileCommit(transportRoot: string, hostname: string): string | null {
76
+ const hostPath = `hosts/${hostname}.md`;
77
+ const r = captureGit(transportRoot, ['log', '--format=%H', '--diff-filter=A', '--', hostPath]);
78
+ if (r.status !== 0 || !r.stdout.trim()) return null;
79
+ // `git log` lists newest first; the last line is the introducing commit.
80
+ const lines = r.stdout.trim().split('\n').filter(Boolean);
81
+ return lines[lines.length - 1] ?? null;
82
+ }
83
+
71
84
  // Repo-relative paths of message files added between `sinceCommit` and
72
85
  // HEAD. Returns null when the commit is unknown to this clone (state dir
73
86
  // copied across transports, history rewritten) — caller falls back to a