@cordfuse/crosstalk 5.0.0-alpha.7 → 6.0.0-alpha.10

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.
Files changed (58) hide show
  1. package/GUIDE-CLI.md +298 -0
  2. package/GUIDE-PROMPTS.md +132 -0
  3. package/README.md +139 -0
  4. package/bin/crosstalk.js +51 -80
  5. package/package.json +9 -5
  6. package/src/activation.ts +104 -0
  7. package/src/actor.ts +29 -4
  8. package/src/attach.ts +1 -1
  9. package/src/channel.ts +8 -21
  10. package/src/chat.ts +52 -115
  11. package/src/dispatch.ts +288 -660
  12. package/src/dlq.ts +89 -136
  13. package/src/init.ts +23 -42
  14. package/src/open.ts +55 -31
  15. package/src/replies.ts +59 -0
  16. package/src/send.ts +87 -72
  17. package/src/state.ts +173 -0
  18. package/src/status.ts +18 -57
  19. package/src/stop.ts +37 -0
  20. package/src/transport.ts +81 -198
  21. package/src/turnq.ts +64 -32
  22. package/src/upgrade.ts +9 -11
  23. package/src/wake.ts +5 -6
  24. package/template/CLAUDE.md +12 -2
  25. package/template/gitignore +4 -0
  26. package/template/upstream/CROSSTALK-VERSION +1 -1
  27. package/template/upstream/CROSSTALK.md +172 -463
  28. package/template/upstream/OPERATOR.md +9 -9
  29. package/template/upstream/PROTOCOL.md +64 -244
  30. package/template/upstream/actors/concierge.md +24 -118
  31. package/src/cursor.ts +0 -48
  32. package/template/.amazonq/rules/crosstalk.md +0 -2
  33. package/template/.continue/rules/crosstalk.md +0 -7
  34. package/template/.cursor/rules/crosstalk.mdc +0 -7
  35. package/template/.github/copilot-instructions.md +0 -2
  36. package/template/.windsurfrules +0 -2
  37. package/template/AGENTS.md +0 -2
  38. package/template/ANTIGRAVITY.md +0 -2
  39. package/template/GEMINI.md +0 -2
  40. package/template/OPENCODE.md +0 -2
  41. package/template/QWEN.md +0 -2
  42. package/template/README.md +0 -22
  43. package/template/local/CROSSTALK.md +0 -4
  44. package/template/upstream/JITTER.md +0 -24
  45. package/template/upstream/actors/cloud-architect.md +0 -83
  46. package/template/upstream/actors/devops-engineer.md +0 -83
  47. package/template/upstream/actors/documentation-engineer.md +0 -107
  48. package/template/upstream/actors/infrastructure-engineer.md +0 -83
  49. package/template/upstream/actors/junior-developer.md +0 -83
  50. package/template/upstream/actors/precise-generalist.md +0 -48
  51. package/template/upstream/actors/product-manager.md +0 -83
  52. package/template/upstream/actors/qa-engineer.md +0 -83
  53. package/template/upstream/actors/security-engineer.md +0 -92
  54. package/template/upstream/actors/senior-generalist-engineer.md +0 -111
  55. package/template/upstream/actors/senior-software-engineer.md +0 -94
  56. package/template/upstream/actors/skeptic.md +0 -89
  57. package/template/upstream/actors/technical-writer.md +0 -89
  58. package/template/upstream/actors/ux-designer.md +0 -83
@@ -1,26 +1,27 @@
1
1
  # Crosstalk
2
2
 
3
- Version: 5.0
3
+ Version: 6.0
4
4
 
5
5
  Crosstalk is a shared file format over git that lets humans and AI agents communicate
6
- asynchronously. The git repository is the message bus. No special software is required
7
- to participate beyond git itself.
6
+ asynchronously across machines. The git repository is the message bus. No special
7
+ software is required to participate beyond git itself.
8
+
9
+ Design rule for this spec: every feature must be explainable in one sentence.
10
+ The runtime records facts at write time; it never reconstructs them by inference
11
+ at read time.
8
12
 
9
13
  ---
10
14
 
11
15
  ## Participants
12
16
 
13
- A Crosstalk transport has two kinds of participants:
14
-
15
- **Humans** — operators and users who post messages directly or via a chat session with an agent. They read replies in the channel and drive the conversation.
16
-
17
- **Machines** — agents (Claude, Codex, agy, etc.) that process messages and reply autonomously. A machine participant is invoked on a schedule or by a human and acts on any unread messages addressed to it.
17
+ **Humans** operators who post messages directly or via a chat tool and read replies.
18
18
 
19
- ### The worker pattern
19
+ **Machines** agents (Claude, Codex, etc.) invoked by a dispatcher to process
20
+ messages addressed to them and reply.
20
21
 
21
- The most common setup is a **worker** — a machine participant that serves both humans and machines. `to: concierge` means "I need something done." The worker receives the request, acts on it, and replies. The sender may be human or machine; the worker does not distinguish.
22
-
23
- Any participant name can fill this role. The operator defines what the worker actually does via its system prompt — Crosstalk only handles the message routing.
22
+ The most common machine participant is a **worker**: `to: concierge` means
23
+ "I need something done." The worker acts and replies. The sender may be human
24
+ or machine; the worker does not distinguish.
24
25
 
25
26
  ---
26
27
 
@@ -28,562 +29,270 @@ Any participant name can fill this role. The operator defines what the worker ac
28
29
 
29
30
  ```
30
31
  <transport>/
31
- upstream/
32
- CROSSTALK.md
33
- CROSSTALK-VERSION
34
- PROTOCOL.md
35
- OPERATOR.md
36
- actors/
37
- <name>.md
38
- local/
39
- CROSSTALK.md
40
- actors/
41
- <name>.md
32
+ upstream/ # runtime-managed: spec, agent orientation, defaults
33
+ CROSSTALK.md # this file
34
+ CROSSTALK-VERSION # must be exactly: 6.0
35
+ PROTOCOL.md # agent orientation prompt
36
+ actors/<name>.md # default actor profiles
37
+ local/ # operator-owned: never touched by the runtime
38
+ actors/<name>.md # custom actor profiles (override upstream by name)
42
39
  hosts/
43
- <alias>.md
40
+ <alias>.md # one per machine running a dispatcher
44
41
  data/
45
- channels/
46
- <guid>/
47
- YYYY/
48
- MM/
49
- DD/
50
- HHMMSSsssZ-<hex>.md
51
- memories/
52
- <YYYYMMDDTHHMMSSsssZ-hex>.md
42
+ channels/<uuid>/
43
+ CHANNEL.md # optional channel metadata
44
+ YYYY/MM/DD/HHMMSSmmmZ-<hex>.md # messages
45
+ memories/<stamp>-<hex>.md # shared persistent notes
53
46
  ```
54
47
 
55
- - `upstream/CROSSTALK.md` this file. The protocol specification. Upstream-owned; operators never modify it.
56
- - `upstream/CROSSTALK-VERSION` plain text file declaring the protocol version. Hard boundary: must be `5.0`. Tracks the protocol generation (`major.minor`); patch-level release tags do not bump it.
57
- - `local/CROSSTALK.md` operator config. Declares `name` and `email` for this agent. Operator-owned; upstream never modifies it.
58
- - `local/actors/` one markdown file per participant. Defines name, skills, and behavioral description.
59
- - `hosts/` — one markdown file per host (machine running the runtime). Declares the host's identity and the actors it can service. Operator-owned; each host owner creates and maintains their own file. See Host files.
60
- - `data/channels/` — one subdirectory per channel, identified by a UUID v4. Discovered by listing the directory at runtime; no configuration needed. To create a channel, create the directory and commit — any participant may do this.
61
- - `data/channels/<guid>/CHANNEL.md` — optional channel metadata file. Declares name, description, and parent channel for subchannels.
62
- - `data/memories/` — shared memory files, readable and writable by any participant.
48
+ That is the complete committed surface. **Dispatcher bookkeeping (cursors, dead-letter
49
+ queue, error logs, lock state) is machine-local state and never lives in the repo**
50
+ it is kept under `$CROSSTALK_STATE_DIR` (default `~/.local/state/crosstalk/<transport-id>/`,
51
+ where `<transport-id>` is derived from the origin URL). The repo carries conversation;
52
+ each machine carries its own progress through it.
63
53
 
64
54
  ---
65
55
 
66
56
  ## Channels
67
57
 
68
- Each channel is a UUID v4 directory under `data/channels/`. Any participant may create a channel by creating the directory and committing.
69
-
70
- ### CHANNEL.md
71
-
72
- An optional `CHANNEL.md` at `data/channels/<guid>/CHANNEL.md` declares human-readable metadata for the channel. Agents load it on entry to understand the channel's purpose and context.
58
+ A channel is a UUID v4 directory under `data/channels/`. Any participant may create
59
+ one by creating the directory and committing. An optional `CHANNEL.md` declares
60
+ metadata:
73
61
 
74
62
  ```
75
63
  ---
76
64
  name: dogfood-sprint
77
65
  created_by: steve
78
- created_at: 2026-05-24T17:00:00.000Z
66
+ created_at: 2026-06-09T17:00:00.000Z
67
+ parent: <uuid> # optional — makes this a subchannel
79
68
  ---
80
69
 
81
- Main channel for the Crosstalk v2 dogfood sprint. Covers spec reviews,
82
- Monte Carlo pi experiments, and multi-agent protocol testing.
83
-
84
- Participants: steve, concierge, architect, qa, security, scrum-master.
70
+ Free-form description. Serves as a mini system prompt scoped to the channel.
85
71
  ```
86
72
 
87
- #### CHANNEL.md frontmatter fields
88
-
89
- | Field | Required | Notes |
90
- |---|---|---|
91
- | `name` | yes | human-readable label; unique within the transport |
92
- | `created_by` | no | actor name who created the channel |
93
- | `created_at` | no | ISO 8601 UTC |
94
- | `parent` | no | GUID of the parent channel — see Subchannels |
73
+ `name` is required and unique within the transport; the other fields are optional.
74
+ A subchannel is just a channel with a `parent:`; it reports back by posting to the
75
+ parent channel. There is no close signal — a finished channel simply goes quiet.
95
76
 
96
- The body is free-form markdown — purpose, participants, behavioral notes. It serves as a mini system prompt scoped to the channel. Agents that do not load `CHANNEL.md` are unaffected.
77
+ ---
97
78
 
98
- ### Subchannels
79
+ ## Messages
99
80
 
100
- A subchannel is a channel spawned from a parent channel to work on a focused problem. It is a normal channel with a `parent:` field in its `CHANNEL.md` declaring the parent's GUID.
81
+ Every message is a markdown file with YAML frontmatter:
101
82
 
102
83
  ```
103
84
  ---
104
- name: pi-estimation
105
- parent: adfe0356-0f71-49a9-80a0-fb76883cd974
106
- created_by: concierge
107
- created_at: 2026-05-24T17:00:00.000Z
85
+ from: alice
86
+ to: concierge
87
+ type: text
88
+ timestamp: 2026-06-09T19:00:00.000Z
108
89
  ---
109
90
 
110
- Subchannel for the Monte Carlo pi estimation fan-out. Four engineers
111
- each throw 1B darts and post results here.
91
+ Message body here.
112
92
  ```
113
93
 
114
- The parent channel can interrupt or feed new data into the subchannel at any time by posting a message there addressed to the relevant agents. Agents scan all channels they participate in — they will pick up the interrupt on their next session open regardless of which channel they are focused on.
94
+ ### Frontmatter fields
115
95
 
116
- The subchannel reports completion by posting back to the parent channel. There is no protocol-level close signal — the subchannel simply goes quiet when the work is done.
96
+ | Field | Required | Notes |
97
+ |---|---|---|
98
+ | `from` | yes | logical actor name — unverified; trust boundary is repo access |
99
+ | `to` | yes | name, list of names, or `all`; may carry `@host` suffix (see Routing) |
100
+ | `type` | yes | always `text` in 6.0 |
101
+ | `timestamp` | yes | ISO 8601 UTC |
102
+ | `re` | no | relPath (or list of relPaths) of the message(s) this one answers — **written by the runtime, never by hand** |
103
+ | `tier` | no | requested model tier for the recipient (see Host files) |
117
104
 
118
- ---
105
+ Readers must ignore unknown fields.
119
106
 
120
- ## Actors
107
+ ### The `re:` field — causality is recorded, not inferred
121
108
 
122
- Each participant in the transport has an actor file in `local/actors/<name>.md`. The filename
123
- matches the participant's `name`. Actor files are readable by any participant — agents
124
- load them to understand who else is in the transport and what they do.
109
+ A message **without** `re:` is a new task. A message **with** `re:` is a reply to the
110
+ message(s) at the listed relPath(s) (paths relative to the channel directory). Like
111
+ `to:`, the field is a string for one target and a list for several — a reply that
112
+ answers a batch records **every** message it answers, so batching never makes an
113
+ answered message look unanswered.
125
114
 
126
- ### Actor file format
115
+ The runtime sets `re:` from facts it directly observes:
127
116
 
128
- ```
129
- ---
130
- name: precise-generalist
131
- description: "Precise, curious, direct the reliable general-purpose voice."
132
- metadata:
133
- author: cordfuse
134
- domain: general
135
- type: actor
136
- alias: Apex
137
- ---
117
+ - When an actor answers via stdout, the runtime writes the reply with `re:` listing
118
+ every message in the dispatched batch from that asker.
119
+ - When a dispatched actor uses `crosstalk send`, the runtime injects the triggering
120
+ relPath(s) into the environment and `send` records them automatically. An actor can
121
+ suppress this (`--new`) to start genuinely new work.
122
+ - Messages written by operators (chat tools, hand-authored) carry no `re:` — they
123
+ are new tasks by definition.
138
124
 
139
- ## Title
140
- Precise, curious, direct. Thinks clearly, speaks plainly.
125
+ Actors never compute or hand-write `re:`. Because the field is set by the machinery
126
+ that already knows the answer, a confused or dishonest actor cannot mislabel a reply
127
+ as a task or vice versa.
141
128
 
142
- ## System Prompt
143
- You are Apex. Precise, curious, direct. You think clearly and speak plainly.
144
- ```
129
+ ### Filenames
145
130
 
146
- ### Actor frontmatter fields
131
+ `data/channels/<uuid>/YYYY/MM/DD/HHMMSSmmmZ-<hex>.md` current UTC time plus a
132
+ hex suffix of at least 8 characters from a CSPRNG. Filenames sort chronologically
133
+ and are collision-free by construction, so concurrent writers on different machines
134
+ never produce git conflicts in message files.
147
135
 
148
- | Field | Required | Values | Notes |
149
- |---|---|---|---|
150
- | `name` | yes | slug | must match the filename stem |
151
- | `description` | yes | short string | one-line summary; used by peer agents and operator tooling |
152
- | `metadata.author` | no | string | who maintains this actor |
153
- | `metadata.domain` | no | string | e.g. `general`, `engineering`, `health` |
154
- | `metadata.type` | no | `actor` | always `actor` for participant files |
155
- | `metadata.alias` | no | string | human-readable name the actor introduces itself by |
156
-
157
- The body is free-form markdown. It serves as the actor's system prompt — behavioral
158
- context loaded by the runtime when dispatching to this participant.
136
+ ---
159
137
 
160
- ### Default actor
138
+ ## Activation — when does a message wake its addressee?
161
139
 
162
- `upstream/actors/precise-generalist.md` ships with every Crosstalk transport.
163
- It is the default participant when no custom actor is configured. Operators override it
164
- by placing a file with the same `name:` in `local/actors/`.
140
+ One rule:
165
141
 
166
- ### Managing actors
142
+ > **A message wakes its addressee if it has no `re:` (a new task), or any `re:`
143
+ > entry points at a message the addressee sent.**
167
144
 
168
- Actors are created, modified, or removed by editing files in `local/actors/` and committing.
169
- The worker handles actor management on behalf of the operator via natural language in any channel.
145
+ Consequences:
170
146
 
171
- If `local/actors/` is absent or empty, agents proceed normally — actor discovery yields no peers.
147
+ - Tasks always wake the actor they address.
148
+ - A reply wakes whoever asked the question, and no one else.
149
+ - A reply addressed to someone who never asked (an FYI, a broadcast copy) is visible
150
+ in the channel but does not wake them — fan-in cannot oscillate.
151
+ - Self-sent messages never wake their sender.
172
152
 
173
- On session open, scan actor files for duplicate `name` values. If a collision is found, halt and notify the operator.
153
+ There are no other wake conditions and no inference. The dispatcher evaluates this
154
+ rule with two field reads.
174
155
 
175
- ### Importing actors from agent-assets
156
+ ---
176
157
 
177
- The worker recognises three natural language intents for importing actors from `cordfuse/agent-assets`:
158
+ ## Delivery semantics
178
159
 
179
- **Browse**
160
+ **At-least-once.** Each dispatcher tracks a per-actor, per-channel cursor (local
161
+ state, not in the repo) recording the git commit the channel was last scanned at —
162
+ "new" means *added to git since that commit*, never "later filename timestamp",
163
+ because messages reach origin in push order, not timestamp order. If a machine
164
+ crashes mid-tick, the next tick re-dispatches anything not yet past the cursor;
165
+ a duplicate reply may land in the channel.
180
166
 
181
- Triggers: "browse actors" | "what actors are available?" | "show me who I can add" | "who do you have?" | "show me [domain] actors" | "any [domain] options?" | "I need someone who [does/knows X]"
167
+ For idempotent work (lookups, computation, advice) duplicates are harmless. For
168
+ non-idempotent side effects, the actor must check the channel for evidence of prior
169
+ completion before acting. Crosstalk does not provide exactly-once semantics.
182
170
 
183
- Worker:
184
- 1. Fetches `https://raw.githubusercontent.com/cordfuse/agent-assets/main/README.md`
185
- 2. Presents the full roster grouped by domain name, alias, one-line description
186
- 3. If a domain or trait was given, filters to matching actors only
187
- 4. Prompts: *"Which would you like to add? Name one or more — or say 'all'."*
188
- 5. Proceeds to the add flow for each selected actor
171
+ **Batched delivery.** When a dispatcher wakes an actor, it hands over ALL pending
172
+ messages addressed to that actor in that channel in a single invocation. One
173
+ activation drains the mailbox a coordinator that fanned out to 10 peers wakes
174
+ once and sees all 10 replies together.
189
175
 
190
- **Add**
176
+ The transport is an **append-only log**. No retraction, no deletion at the protocol
177
+ level. Retention is the operator's concern at the git/storage layer.
191
178
 
192
- Triggers: "add [name]" | "add a [role]" | "bring in [name]" | "I want [name/role]" | "add [name] from mtx"
179
+ ---
193
180
 
194
- Worker:
195
- 1. If a name was given directly, fetches `https://raw.githubusercontent.com/cordfuse/agent-assets/main/actors/{name}.md`
196
- 2. If a role or description was given, matches against the README roster and confirms with the operator before fetching: *"Closest match: [Name] — [description]. Add them? (yes / browse more)"*
197
- 3. Writes to `local/actors/{name}.md`
198
- 4. Commits: `actors: add {name} from agent-assets`
199
- 5. Confirms: *"[Name] added. They're now available as a participant."*
181
+ ## Actors
200
182
 
201
- On name collision with an existing file: *"You already have [name] — overwrite? (yes / skip)"*
202
- If not found: *"No actor named [name] in agent-assets. Say 'browse actors' to see the full roster."*
183
+ Each participant has a profile at `local/actors/<name>.md` (operator-owned) or
184
+ `upstream/actors/<name>.md` (defaults; `local/` wins on name collision). The body
185
+ is the actor's system prompt.
203
186
 
204
- **Sync**
187
+ ```
188
+ ---
189
+ name: concierge
190
+ description: "General-purpose worker and coordinator."
191
+ ---
205
192
 
206
- Triggers: "sync actors" | "update actors" | "are my actors up to date?" | "refresh actors from mtx"
193
+ ## System Prompt
194
+ You are the general-purpose worker in this Crosstalk transport. ...
195
+ ```
207
196
 
208
- Worker:
209
- 1. Scans `local/actors/` for files whose frontmatter has `author: cordfuse`
210
- 2. For each, fetches the latest from `https://raw.githubusercontent.com/cordfuse/agent-assets/main/actors/{name}.md`
211
- 3. Overwrites only cordfuse-authored files — never touches operator-created actors
212
- 4. Commits all updates in a single commit: `actors: sync from agent-assets`
213
- 5. Reports: *"Updated N actors from agent-assets: [list]."* If nothing changed: *"All actors are current."*
197
+ `name` (matching the filename stem) and `description` are required; the rest of the
198
+ frontmatter is free. Actors are added, edited, and removed by committing files.
214
199
 
215
200
  ---
216
201
 
217
202
  ## Host files
218
203
 
219
- A host file at `hosts/<alias>.md` declares a machine that runs the Crosstalk runtime and the actors it can service. Host files are operator-owned — each operator creates and maintains their own. They are committed to the transport so all participants can discover what hosts and capabilities exist.
220
-
221
- ### Host file format
204
+ A host file at `hosts/<alias>.md` declares one machine running a dispatcher and the
205
+ actors it serves. Each operator commits and maintains their own.
222
206
 
223
207
  ```
224
208
  ---
225
209
  alias: cachy
226
210
  hostname: steve-cachyos
227
211
  actors:
212
+ concierge:
213
+ claude: claude --print --dangerously-skip-permissions
228
214
  junior-developer:
229
215
  haiku:
230
216
  cli: claude --model claude-haiku-4-5 --print --dangerously-skip-permissions
231
217
  count: 5
232
- flash: gemini --model gemini-2.5-flash -p --yolo
233
- senior-developer:
234
- opus: claude --model claude-opus-4-7 --print --dangerously-skip-permissions
235
218
  ---
236
219
  ```
237
220
 
238
- ### Host frontmatter fields
239
-
240
221
  | Field | Required | Notes |
241
222
  |---|---|---|
242
- | `alias` | yes | human-readable identity; used in `actor@host` addressing; operator-chosen, free-form |
243
- | `hostname` | no | OS hostname (`os.hostname()`); used for auto-detection |
244
- | `username` | no | OS username (`os.userInfo().username`); used for multi-user host auto-detection |
245
- | `actors` | yes | map of actor names to tier declarations |
246
-
247
- ### Tier declarations
248
-
249
- Each actor entry maps tier names to CLI commands. A tier is a named model/provider slot:
223
+ | `alias` | yes | the host's name in `actor@host` addressing |
224
+ | `hostname` | no | OS hostname, used for dispatcher auto-detection |
225
+ | `actors` | yes | actor tier map |
250
226
 
251
- ```yaml
252
- actors:
253
- junior-developer:
254
- haiku: # tier name operator-defined label
255
- cli: claude --model claude-haiku-4-5 --print --dangerously-skip-permissions
256
- count: 5 # parallel workers; default: 1
257
- flash: gemini --model gemini-2.5-flash -p --yolo # shorthand — count defaults to 1
258
- ```
259
-
260
- - Tier names are operator-defined strings (`haiku`, `flash`, `opus`, etc.). The concierge uses them for routing decisions.
261
- - The `cli:` string is the shell command the runtime uses to invoke the agent.
262
- - `count:` sets the number of parallel workers for this tier. Omit or set to `1` for a single worker. The shorthand (bare string) is equivalent to `count: 1`.
263
- - Multiple tiers of the same actor on the same host form a single instance group for dispatch purposes. The total group size is the sum of all tier counts for that actor.
264
-
265
- ### Actor@host addressing
266
-
267
- Messages may target a specific host by appending `@<alias>` to the actor name:
268
-
269
- ```yaml
270
- to: junior-developer@cachy # only cachy's runtime dispatches
271
- to: senior-developer@mac # only mac's runtime dispatches
272
- to: concierge # all hosts with a concierge actor dispatch (bare name = broadcast)
273
- ```
274
-
275
- Bare actor names are backward-compatible — any host with that actor services the message, subject to the instance group selection rule.
276
-
277
- ### Host routing table
227
+ A **tier** is a named CLI slot. The bare-string shorthand means `count: 1`; the
228
+ object form adds `count:` (parallel invocations) per tier. Tier names are
229
+ operator-defined labels (`haiku`, `opus`, `flash`); senders may request one with
230
+ the `tier:` message field, and the dispatcher falls back to the first declared
231
+ tier when the requested one doesn't exist.
278
232
 
279
- On session open, the concierge reads `hosts/` to build a routing table: which actors are available, on which hosts, at which tiers. This allows the concierge to make informed routing decisions before sending messages.
280
-
281
- ### Startup behavior
282
-
283
- On runtime startup, the runtime scans `hosts/` to find its own host file. Auto-detection precedence:
284
-
285
- 1. **Explicit override:** if `host:` is set in the local config, find the file where `alias:` matches exactly.
286
- 2. **Username + hostname:** if `username:` and `hostname:` both match the machine's `os.userInfo().username` and `os.hostname()`. Use this for multi-user hosts where multiple operators share a machine.
287
- 3. **Bare hostname:** if only `hostname:` matches. Backward-compatible fallback for single-user machines.
288
- 4. **No match:** log clearly, idle without dispatching, and do not crash. The log message must include the identity attempted and the path scanned so the operator can diagnose the mismatch.
289
-
290
- A user can always override their alias by setting `host: <alias>` in their local config — this bypasses all auto-detection.
291
-
292
- **Tier validation:** if a message is addressed `to: actor@host` and this runtime is the target host but has no matching actor declared, log a warning and skip. Do not crash.
293
-
294
- ### Host file ownership
295
-
296
- Each operator commits only their own host file. The protocol does not enforce this — it is a convention. Trust at the repository level: any participant with write access can modify any host file. Operators who need stronger guarantees must secure the repository itself.
233
+ On startup a dispatcher finds its own host file by matching `hostname:` (or via an
234
+ explicit `--host <alias>` override). No match → log clearly and idle; never crash.
297
235
 
298
236
  ---
299
237
 
300
- ## Your identity
301
-
302
- Your `name` and `email` are declared in `local/CROSSTALK.md`, which uses this format:
303
-
304
- ```
305
- ---
306
- name: concierge
307
- email: concierge@crosstalk.local
308
- ---
309
- ```
310
-
311
- Set the matching git identity once by running these commands inside the transport repo:
312
-
313
- ```
314
- git config user.name "concierge"
315
- git config user.email "concierge@crosstalk.local"
316
- ```
317
-
318
- Do not use `--global` — this must be repo-local so it does not overwrite the
319
- operator's own git identity. Names are free-form: `alice`, `concierge`, `ops-bot`.
320
- No taxonomy, no hierarchy. Names need not be unique — see Instance groups.
321
-
322
- ### Instance groups
323
-
324
- A name may be shared by multiple participants. They form an **instance group**: addressable by the shared name, but only one instance dispatches per message.
325
-
326
- When a message is addressed to a shared name, every instance reads it. To avoid duplicate dispatch, each instance independently applies the same deterministic selection rule:
327
-
328
- ```
329
- index = sha256(message-relPath) read as 32-bit big-endian unsigned, mod group-size
330
- chosen = instance at position `index` (0-based, in the group's local ordering)
331
- ```
332
-
333
- The instance whose own position equals `index` dispatches; the rest skip and advance their cursor. Only the chosen instance posts a reply and receipt.
334
-
335
- **The selection function is normative** — all runtimes must use this exact computation so that instances reach the same decision without coordination.
336
-
337
- **Local ordering is runtime-defined.** Single-operator scope: the runtime orders instances by config position (or any other stable local ordering) and is consistent within its own process. Multi-operator scope (instances spread across operators sharing one transport): not yet specified — runtimes that span multiple operators may double-dispatch until a roster-discovery mechanism is added.
338
-
339
- Single-instance semantics are recovered by giving each participant a unique name. Operators who don't want instance groups simply don't share names.
340
-
341
- ### Identity model
342
-
343
- `from:` identifies the logical actor. The git commit author identifies the session
344
- that wrote the file.
345
-
346
- `from:` and the git commit author are both unverified strings. Identity is not enforced at the protocol level — the trust boundary is repo access. Operators who need verified identity must secure the repository itself.
238
+ ## Routing
347
239
 
348
- ### Sub-agents
240
+ The `to:` field accepts:
349
241
 
350
- A worker session may invoke other actors as sub-agents internally and commit
351
- their responses on their behalf. In this case:
242
+ - `to: concierge` bare name. Every host that declares the actor dispatches it.
243
+ - `to: junior-developer@cachy` narrowed to the host whose `alias` is `cachy`;
244
+ other hosts skip it (and log the skip, so wrong-host routes are visible).
245
+ - `to: [a, b@mac]` — lists mix freely.
246
+ - `to: all` — every participant.
352
247
 
353
- - `from:` is set to the logical actor (e.g. `architect`)
354
- - `via:` is set to the committing session (e.g. `concierge`)
355
- - The git commit author is the worker's git identity
248
+ The actor name is everything before the `@`; the host alias is everything after.
249
+ The `re:` activation rule ignores host suffixes only addressing honors them.
356
250
 
357
- This allows a single scheduled job to drive all actor activity. The operator configures
358
- only the worker's git identity. Sub-agents are invoked inside the worker's session no separate git identity or scheduler required per actor.
359
-
360
- Sub-agents do not require actor files. An `actors/<name>.md` may exist to document the sub-agent, but it is not required for the pattern to work.
361
-
362
- ```
363
- ---
364
- from: architect
365
- via: concierge
366
- to: steve
367
- type: text
368
- timestamp: 2026-05-24T14:00:00.000Z
369
- ---
370
-
371
- Sub-agent response here.
372
- ```
373
-
374
- ### Peer agents
375
-
376
- Each participant runs as its own independent process with its own git clone, git identity, and scheduler. No `via:` field — every message is committed directly by the actor that wrote it. Push conflicts are resolved by exponential backoff (see On session open, step 6).
377
-
378
- **Sub-agents vs peer agents:**
379
-
380
- | | Sub-agents | Peer agents |
381
- |---|---|---|
382
- | Parallelism | Yes — worker fans out concurrently | Yes — independent processes |
383
- | Model diversity | Yes — worker picks model per invocation | Yes — set at deployment per process |
384
- | Git authorship | Single committer (the worker) | One committer per actor |
385
- | Failure isolation | Worker is a single point of failure | Agents fail independently |
386
- | Deployment | One scheduler, one clone | One scheduler and clone per agent |
387
- | Push conflicts | None — single writer | Possible — resolved by backoff |
388
-
389
- Use sub-agents when a single scheduled job should drive all actor activity and git authorship per actor is not required. Use peer agents when agents are deployed across different machines or providers, or when independent failure domains and per-actor git authorship matter.
251
+ Use bare names for work-pool patterns where any machine will do; use `@host` when
252
+ the orchestration depends on which machine runs the work. This addressing is the
253
+ entirety of Crosstalk's multi-host model.
390
254
 
391
255
  ---
392
256
 
393
- ## Message format
257
+ ## Identity and trust
394
258
 
395
- Every message is a markdown file with YAML frontmatter:
396
-
397
- ```
398
- ---
399
- from: alice
400
- to: concierge
401
- type: text
402
- timestamp: 2026-05-23T19:00:00.000Z
403
- ---
404
-
405
- Message body here.
406
- ```
407
-
408
- Read message files directly with your agent's native file-reading capability — the format is plain markdown with YAML frontmatter, intended to be inspected as text. No parser, interpreter, or shelling out for `cat`/`sed`/`awk` is required.
409
-
410
- ### Frontmatter fields
411
-
412
- | Field | Required | Values | Notes |
413
- |---|---|---|---|
414
- | `from` | yes | free-form name | logical actor identity — see Identity model |
415
- | `to` | yes | name, list of names, or `all` | `all` targets every participant |
416
- | `type` | yes | `text`, `read` | see Message types |
417
- | `timestamp` | yes | ISO 8601 UTC | |
418
- | `ref` | conditional | relPath of original message | required when `type: read`; path relative to `data/channels/<guid>/` |
419
- | `via` | no | free-form name | session that committed on behalf of `from` — see Sub-agents |
420
-
421
- Operators may add fields — readers must ignore unknown fields.
422
-
423
- ### To field
424
-
425
- - Single recipient: `to: concierge`
426
- - Multiple recipients: `to: [concierge, ops-bot]`
427
- - Broadcast: `to: all`
428
- - Host-targeted: `to: junior-developer@cachy` — only the named host dispatches; see Host files
429
- - Senders do not receive their own messages. Self-exclusion applies at scan time — agents skip any message where `from:` matches their own name.
430
- - A name may be shared by multiple participants — see Instance groups.
431
-
432
- ### Message types
433
-
434
- **`type: text`** — a normal message. Body is the message content.
435
-
436
- **`type: read`** — a read receipt. The `ref` field contains the relPath of the
437
- acknowledged message. Body is empty or omitted.
438
-
439
- ```
440
- ---
441
- from: concierge
442
- to: alice
443
- type: read
444
- timestamp: 2026-05-23T19:02:00.000Z
445
- ref: 2026/05/23/190100000Z-b2c3d4e5.md
446
- ---
447
- ```
448
-
449
- Read receipts are optional. Senders cannot require them. Address the receipt to the
450
- original sender (`from:` of the original message) regardless of whether the original
451
- was targeted, listed, or broadcast. Never address a receipt to `all`.
452
-
453
- Each addressed recipient in a list-targeted message sends its own independent receipt to the original sender.
454
-
455
- Never post a receipt for a `type: read` message.
456
-
457
- A receipt with a `ref:` pointing to a nonexistent message path is treated as malformed — skip silently.
458
-
459
- ---
460
-
461
- ## Privacy and trust model
462
-
463
- `to` is a routing hint, not an access control boundary. Every message is visible to anyone with repo access. There are no whisper or ephemeral message types.
464
-
465
- `from:` is an unverified declaration — the protocol does not enforce identity. The trust boundary is repo access: anyone who can push to the repository can write any `from:` value. Operators who require confidentiality or verified identity must secure the repository itself.
466
-
467
- ---
468
-
469
- ## Append-only log
470
-
471
- The transport is an immutable, append-only log. There is no `type: retract` or channel-close message. Deletion is out of scope — operators who need retention limits must manage that at the git or storage layer.
472
-
473
- ---
474
-
475
- ## Sessions
476
-
477
- At startup, generate a random GUID. This is your session ID for this invocation.
478
- Use it when writing memory files. Sessions are self-issued — no registry, no daemon.
259
+ `from:` is an unverified string. The trust boundary is repository access: anyone who
260
+ can push can claim any name. `to:` is a routing hint, not access control — every
261
+ message is visible to anyone with repo access. Operators who need confidentiality
262
+ or verified identity must secure the repository itself.
479
263
 
480
264
  ---
481
265
 
482
266
  ## Memories
483
267
 
484
- Memory files are shared, persistent knowledge any participant can read and write.
485
-
486
- ### Memory file format
487
-
488
- Filename: `YYYYMMDDTHHMMSSsssZ-<hex>.md` — UTC millisecond timestamp + hex suffix
489
- for collision resistance. Sorts chronologically. The hex suffix must be at least
490
- 8 characters, generated from a CSPRNG.
268
+ Shared persistent notes any participant may read or write, at
269
+ `data/memories/YYYYMMDDTHHMMSSmmmZ-<hex>.md`:
491
270
 
492
271
  ```
493
272
  ---
494
273
  from: concierge
495
- timestamp: 2026-05-23T19:00:00.000Z
274
+ timestamp: 2026-06-09T19:00:00.000Z
496
275
  subject: Steve prefers TypeScript for all new tooling
497
- scope: global
498
- tags: [preferences, tooling]
499
- session: 7f3a2b1c-9e4d-4f8a-b6c2-1d5e8f0a3c7b
276
+ scope: global # or a channel uuid
277
+ supersedes: <filename> # optional — replaces an earlier memory
500
278
  ---
501
279
 
502
- Strong durable preference — never propose Python. Default to TypeScript/Bun, Go, or Rust.
280
+ Body.
503
281
  ```
504
282
 
505
- ### Memory frontmatter fields
506
-
507
- | Field | Required | Values | Notes |
508
- |---|---|---|---|
509
- | `from` | yes | free-form name | logical actor identity |
510
- | `via` | no | free-form name | session that committed on behalf of `from` — see Sub-agents |
511
- | `timestamp` | yes | ISO 8601 UTC | |
512
- | `subject` | yes | short string | descriptive label; used for filtering, not update detection |
513
- | `scope` | no | `global` or channel GUID | `global` if omitted |
514
- | `tags` | no | list of strings | free-form filtering |
515
- | `session` | no | session GUID | ties memory to the writing session |
516
- | `supersedes` | no | filename of original memory | declares this memory replaces an earlier one — see Updating memories |
517
-
518
- Memory files are agent-agnostic — any participant may read or write them. Do not
519
- use your model's native memory system; `data/memories/` is the memory system.
520
-
521
- ### Updating memories
522
-
523
- To update a memory, write a new memory file with `supersedes: <filename-of-original>`. When loading memories, collect all filenames referenced by any `supersedes` field. Skip any memory whose filename appears in that set — it has been superseded. If two memories both supersede the same file, the newer timestamp wins. For same-timestamp ties, lexicographic filename order is the tiebreaker.
524
-
525
- `subject` is descriptive only — it plays no role in update detection.
526
-
527
- The `scope` field must be either `global` or a channel GUID from `data/channels/`. Session GUIDs are not valid scope values.
528
-
529
- ---
530
-
531
- ## On session open
532
-
533
- 1. Read `local/CROSSTALK.md`. If absent, halt and notify the operator. If `name` or `email` is still `CONFIGURE`, halt and notify the operator. Do not load memories, process messages, or write to `data/`. (Writing to `local/` to establish identity — e.g. via an operator-driven onboarding flow — is permitted, since identity establishment is the only way out of this state.)
534
- 2. Read `upstream/CROSSTALK-VERSION`. If missing or not exactly `5.0`, halt and notify the operator. Do not load memories, process messages, or write to `data/`.
535
- 2a. Read `hosts/` if present. Build a routing table of available actors per host. If the directory is absent or empty, routing table is empty — proceed normally.
536
- 3. Run `git pull` to sync the transport.
537
- 4. Load global memories from `data/memories/` where `scope` is `global` or absent. Filter by `subject` and `tags`.
538
- 5. For each channel in `data/channels/`:
539
- a. Load memories scoped to this channel GUID.
540
- b. Scan for unread messages addressed to you (see Reading messages).
541
- c. Process and reply. Only act on `type: text` messages — `type: read` messages require no reply and no further action.
542
- d. Channel processing order across multiple channels is implementation-defined.
543
- 6. Push immediately. If rejected, read backoff config from `local/JITTER.md` (falls back to `upstream/JITTER.md`). If neither file exists, use defaults: base=100ms, ceiling=5000ms. Run `git pull --rebase`, wait `random(0, min(ceiling, base * 2^attempt))`, then retry. After rebasing, push what was already composed — do not re-scan. Message filenames are unique — rebase conflicts should not occur.
283
+ When loading, skip any memory named in another memory's `supersedes:`. Agents use
284
+ `data/memories/` instead of their model-native memory systems.
544
285
 
545
286
  ---
546
287
 
547
- ## Reading messages
548
-
549
- For each message file in `data/channels/<guid>/`, parsed chronologically:
550
-
551
- 1. Check `to:` — process if your name appears (bare or as `name@your-host-alias`) or `to` is `all`; otherwise skip. If the target includes a `@host` suffix that does not match your host alias, skip. If your name is shared with other participants (an instance group), apply the deterministic selection rule and skip unless you are the chosen member — see Instance groups.
552
- 2. Check for an existing `type: read` receipt from you with a matching `ref:`. If found, skip — already processed.
553
- 3. If unread: process it, post a reply, then post a `type: read` receipt.
554
-
555
- Your `type: read` receipts are your cursor. No external state needed. On first run, all messages are unread — process them all.
556
-
557
- Each addressed agent receipts independently. A receipt from another agent does not mark a message as read for you.
558
-
559
- Actions must be idempotent. If a push fails after processing but before the receipt is committed, the message will be reprocessed on the next run.
560
-
561
- ---
562
-
563
- ## Writing messages
564
-
565
- Create `data/channels/<guid>/YYYY/MM/DD/HHMMSSsssZ-<hex>.md` with the current UTC time.
566
- The hex suffix must be at least 8 characters, generated from a CSPRNG.
567
- Include required frontmatter: `from`, `to`, `type`, `timestamp`. Commit and push
568
- under your configured git identity.
569
-
570
- Write all replies for the session before pushing. Push once per session, not once per message.
571
-
572
- ---
573
-
574
- ## What to ignore
575
-
576
- ### Skip silently
577
-
578
- - Messages not addressed to you and not `to: all`.
579
- - Messages you have already acknowledged with a `type: read` receipt.
580
- - `type: read` messages — never post a receipt for one.
581
- - Files outside `data/channels/` and `data/memories/`.
582
- - Unknown frontmatter fields.
583
- - Your model's native memory system.
288
+ ## Coordination
584
289
 
585
- ### Skip and warn the operator
290
+ Git is self-coordinating: filenames are collision-free and non-fast-forward pushes
291
+ are rejected and retried with `git pull --rebase`. A transport therefore works with
292
+ no coordinator at all.
586
293
 
587
- - Messages where `from:` matches your own name indicates a misconfigured identity.
588
- - Malformed messages files missing required frontmatter fields or with invalid field values. Log the file path.
589
- - Read receipts with a `ref:` pointing to a nonexistent message path — indicates a bug in the writer.
294
+ Dispatchers MAY use a turn coordinator (cordfuse/turnq) to reduce push contention
295
+ locally via file lock, or across hosts via a shared turnq server. Coordination is
296
+ **advisory**: a dispatcher waits for its turn with a bounded timeout and proceeds
297
+ anyway on timeout or coordinator failure, letting git arbitrate. A coordinator
298
+ outage may cost push retries; it can never stall message processing.