agent-coord-mcp 0.26.21 → 0.26.23

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 (71) hide show
  1. package/dist/capabilities.js +270 -2
  2. package/dist/capabilities.js.map +1 -1
  3. package/dist/closing-line.js +83 -0
  4. package/dist/closing-line.js.map +1 -0
  5. package/dist/commit-cite.js +55 -0
  6. package/dist/commit-cite.js.map +1 -0
  7. package/dist/gated-head.js +67 -20
  8. package/dist/gated-head.js.map +1 -1
  9. package/dist/server-spread.js +195 -0
  10. package/dist/server-spread.js.map +1 -0
  11. package/dist/server.js +2 -2
  12. package/dist/server.js.map +1 -1
  13. package/dist/store.js +32 -0
  14. package/dist/store.js.map +1 -1
  15. package/dist/tools/away.js +67 -7
  16. package/dist/tools/away.js.map +1 -1
  17. package/dist/tools/board-ref.js +44 -4
  18. package/dist/tools/board-ref.js.map +1 -1
  19. package/dist/tools/event-kinds.js +5 -1
  20. package/dist/tools/event-kinds.js.map +1 -1
  21. package/dist/tools/events.js +40 -4
  22. package/dist/tools/events.js.map +1 -1
  23. package/dist/tools/herdr-delivery.js +99 -0
  24. package/dist/tools/herdr-delivery.js.map +1 -0
  25. package/dist/tools/messaging.js +72 -6
  26. package/dist/tools/messaging.js.map +1 -1
  27. package/dist/tools/record-events.js +85 -5
  28. package/dist/tools/record-events.js.map +1 -1
  29. package/dist/tools/records.js +310 -44
  30. package/dist/tools/records.js.map +1 -1
  31. package/dist/tools/registry.js +67 -3
  32. package/dist/tools/registry.js.map +1 -1
  33. package/dist/tools/seat-build.js +182 -0
  34. package/dist/tools/seat-build.js.map +1 -0
  35. package/dist/tools/shared.js.map +1 -1
  36. package/dist/tools/stall.js +1095 -18
  37. package/dist/tools/stall.js.map +1 -1
  38. package/dist/tools/transport.js +71 -3
  39. package/dist/tools/transport.js.map +1 -1
  40. package/dist/tools/worktrees.js +14 -0
  41. package/dist/tools/worktrees.js.map +1 -1
  42. package/dist/transports/herdr.js +297 -0
  43. package/dist/transports/herdr.js.map +1 -0
  44. package/dist/transports/index.js +10 -4
  45. package/dist/transports/index.js.map +1 -1
  46. package/package.json +1 -1
  47. package/scripts/coord-attention-clock.mjs +2 -0
  48. package/scripts/coord-stall-clock.mjs +52 -11
  49. package/src/capabilities.ts +284 -2
  50. package/src/closing-line.ts +85 -0
  51. package/src/commit-cite.ts +58 -0
  52. package/src/gated-head.ts +128 -26
  53. package/src/server-spread.ts +233 -0
  54. package/src/server.ts +2 -2
  55. package/src/store.ts +32 -0
  56. package/src/tools/away.ts +82 -9
  57. package/src/tools/board-ref.ts +70 -3
  58. package/src/tools/event-kinds.ts +19 -2
  59. package/src/tools/events.ts +42 -4
  60. package/src/tools/herdr-delivery.ts +87 -0
  61. package/src/tools/messaging.ts +71 -6
  62. package/src/tools/record-events.ts +78 -5
  63. package/src/tools/records.ts +316 -44
  64. package/src/tools/registry.ts +68 -4
  65. package/src/tools/seat-build.ts +201 -0
  66. package/src/tools/shared.ts +22 -0
  67. package/src/tools/stall.ts +1266 -23
  68. package/src/tools/transport.ts +69 -2
  69. package/src/tools/worktrees.ts +13 -0
  70. package/src/transports/herdr.ts +311 -0
  71. package/src/transports/index.ts +10 -4
@@ -1,11 +1,12 @@
1
1
  import { detachAgentTool } from "./transport.js";
2
- import { isLocallyProbeable, isRemoteTmuxKind, targetOf } from "../transports/index.js";
2
+ import { isLocallyProbeable, isRemoteTmuxKind, targetOf, HERDR, HerdrTransport, activeTransport } from "../transports/index.js";
3
3
  import { readAway, secondCoordinatorRefusal } from "./away.js";
4
4
  import { randomUUID } from "node:crypto";
5
- import { existsSync, openSync, watch } from "node:fs";
5
+ import { existsSync, openSync, statSync, watch } from "node:fs";
6
6
  import { promises as fsp } from "node:fs";
7
7
  import { spawn, spawnSync } from "node:child_process";
8
8
  import { fileURLToPath } from "node:url";
9
+ import { detectSpread, installedBuild } from "../server-spread.js";
9
10
  import { z } from "zod";
10
11
  import path from "node:path";
11
12
  import {
@@ -53,7 +54,8 @@ import {
53
54
  type RoomRegistry,
54
55
  type SessionBinding,
55
56
  } from "../store.js";
56
- import { recordAuthorityFor, resolveRole, CANONICAL_ROLE_IDS, roleInputSchema, type RoleArg } from "../roles.js";
57
+ import { recordAuthorityFor, resolveRole, CANONICAL_ROLE_IDS, roleInputSchema, isHuman, type RoleArg } from "../roles.js";
58
+ import { readHumans, recordHuman } from "../store.js";
57
59
  import {
58
60
  type AgentEntry,
59
61
  type AgentRegistry,
@@ -180,6 +182,10 @@ export async function registerTool(args: {
180
182
  return current;
181
183
  });
182
184
  const entry = reg[args.agentId];
185
+ // ⟨q-178878aa⟩ — a HUMAN registration is recorded durably: the registry entry itself is
186
+ // evicted after EVICT_MS (no heartbeat, no transport), and the send path's exemption
187
+ // must still see the human after that.
188
+ if (isHuman(entry)) await recordHuman(args.agentId, { displayName: entry.role, by: `register:${args.agentId}` });
183
189
  // Echo the record types this role may and may not emit. Record authority is
184
190
  // otherwise invisible until the first typed send is refused mid-work — this
185
191
  // is how an agent whose role owns `go`/`scope`/`verdict` finds out at
@@ -287,6 +293,23 @@ export async function heartbeatTool(args: { agentId: string }) {
287
293
  return current;
288
294
  }
289
295
  current[args.agentId].lastHeartbeat = Date.now();
296
+ /*
297
+ * ⛔ STAMP THE ANSWERING PROCESS — `⟨q-cec42e20⟩`. This runs INSIDE the server that
298
+ * is answering, which is the only place these two facts exist: `capabilities`
299
+ * reports them solely to its own caller, and no seat can query another seat's
300
+ * server at all. Publishing them here is what makes a cross-seat comparison
301
+ * possible without every seat having to volunteer it in prose.
302
+ *
303
+ * `startedAt` is derived from uptime rather than read from a file: an mtime tracks
304
+ * writes (a reinstall of identical bytes moves it) while uptime is a fact about
305
+ * THIS process.
306
+ */
307
+ current[args.agentId].serverPid = process.pid;
308
+ current[args.agentId].serverStartedAt = Date.now() - Math.round(process.uptime() * 1000);
309
+ // ⛔ WHAT this process is executing, not what it is labelled. Resolved from this
310
+ // module's own URL, so a server running a dev `dist/` says so instead of inheriting
311
+ // the installed path — the case that made a 3-build fleet read AGREED.
312
+ current[args.agentId].serverModule = installedBuild(import.meta.url, statSync)?.module;
290
313
  return current;
291
314
  });
292
315
  if (missing) return { ok: false, error: `agent '${args.agentId}' not registered` };
@@ -331,6 +354,8 @@ export async function listAgentsTool() {
331
354
  // heartbeat. This stamp only ever overwrote a true value with a
332
355
  // simultaneous one.
333
356
  if (liveTransports.has(id)) continue;
357
+ // A human has no heartbeat to go stale; evicting one is how the exemption went blind.
358
+ if (isHuman(entry)) continue;
334
359
  if (now - entry.lastHeartbeat > EVICT_MS) {
335
360
  evicted.push(id);
336
361
  delete current[id];
@@ -417,7 +442,33 @@ export async function listAgentsTool() {
417
442
  "granted to the SENDER and paid by every READER: their multi-line messages cannot be slimmed. " +
418
443
  "A rising share means the rule is decaying.",
419
444
  };
420
- return { agents, evicted, proseOnly };
445
+ /*
446
+ * ⛔⛆ AND WHETHER THOSE SEATS AGREE ABOUT WHAT THEY ARE RUNNING — `⟨q-cec42e20⟩`.
447
+ *
448
+ * Reported HERE because this is the verb that already answers "who is live", and the
449
+ * spread is a property of that same population: a reader asking who is on the bus is
450
+ * exactly the reader who needs to know their servers are not the same build.
451
+ *
452
+ * ⚠ IT NEVER REPORTS AGREEMENT IT DID NOT ESTABLISH. A seat that has not stamped its
453
+ * identity is UNCOMPARABLE and poisons the verdict to CANNOT_COMPARE rather than being
454
+ * dropped from the population — because "every seat I could read agrees" and "the
455
+ * fleet agrees" are different claims, and only the second is what a reader will act on.
456
+ */
457
+ const spread = detectSpread(
458
+ Object.values(reg)
459
+ .filter((a) => liveTransports.has(a.agentId) || now - a.lastHeartbeat < STALE_MS)
460
+ .map((a) => ({
461
+ agentId: a.agentId,
462
+ serverPid: a.serverPid,
463
+ serverStartedAt: a.serverStartedAt,
464
+ serverModule: a.serverModule,
465
+ })),
466
+ installedBuild(import.meta.url, statSync),
467
+ );
468
+
469
+ // ⟨q-178878aa⟩ — the humans the bus knows: visible here, read at send time, never evicted.
470
+ const humans = Object.entries(await readHumans()).map(([id, h]) => ({ id, ...h }));
471
+ return { agents, evicted, proseOnly, humans, serverSpread: spread };
421
472
  }
422
473
 
423
474
  /**
@@ -474,6 +525,19 @@ export function isMarkerLive(marker: TransportMarker, reg: AgentRegistry, now: n
474
525
  const entry = reg[marker.agentId];
475
526
  return !!entry && now - entry.lastHeartbeat < STALE_MS;
476
527
  }
528
+ // Phase 5.4 Task 4 — a herdr marker has no pid (0): liveness is the PANE, asked of
529
+ // herdr itself. "Could not ask" keeps the marker (unknown is not dead); only herdr
530
+ // saying pane_not_found lets the registry drop it.
531
+ if (marker.transport === HERDR) {
532
+ const t = targetOf(marker);
533
+ if (!t) return false;
534
+ // Ask through the WIRED herdr transport (its runner is what tests inject and what the
535
+ // fleet configured). A server with no herdr transport wired cannot ask, and "cannot
536
+ // ask" keeps the marker: the herdr-configured server reaps its own dead panes.
537
+ const active = activeTransport();
538
+ const exists = active?.kind === HERDR && active instanceof HerdrTransport ? active.paneExists(t) : null;
539
+ return exists !== false;
540
+ }
477
541
  return isPidAlive(marker.pid);
478
542
  }
479
543
 
@@ -0,0 +1,201 @@
1
+ /*
2
+ * ⟨q-8a3f1c05⟩ — A HALF-RESTARTED SEAT IS A STATEMENT, NOT AN INFERENCE.
3
+ *
4
+ * Each seat runs TWO long-lived processes from the installed tree: the MCP server
5
+ * (answers `capabilities`) and the tmux pusher (`hooks/tmux-pusher.mjs --agent <id>`,
6
+ * pastes into the pane). A restart cycles the server and leaves the pusher, so a seat
7
+ * comes back running new server code behind an old pusher — fully responsive, `online:
8
+ * true`, and invisible to every instrument that reads the server. Measured 2026-09-14
9
+ * during the staged restart: stage 1's pusher was three days older than its server and
10
+ * was found only because that seat happened to look; a stale pusher still delivers.
11
+ *
12
+ * THE INSTRUMENT ALREADY EXISTED AND NOBODY CALLED IT: pushers self-identify on the
13
+ * command line (`--agent <id>`), so `ps` + `--agent` + the installed HOOK's mtime is a
14
+ * per-seat pusher-staleness probe. This module states it, per seat, beside the server
15
+ * half — in words, not a pair of timestamps a reader has to subtract.
16
+ *
17
+ * THE HOOK'S OWN MTIME, NEVER A SIBLING'S: `hooks/tmux-pusher.mjs` is the file the
18
+ * pusher loads. `package.json`, `dist/…` and the hook all carry the same instant under
19
+ * a whole-tree install, but an install that rewrites some files and not others breaks
20
+ * that, and nothing checks it. So the pusher half reads the hook file directly.
21
+ *
22
+ * THE LIMIT THIS CARRIES RATHER THAN SOLVES: servers carry no `--agent`, so the server
23
+ * side cannot be keyed to a seat from `ps`. Only the ANSWERING process knows its own
24
+ * start time (`answeredBy.pid`). For every other seat the server half is reported as
25
+ * UNOBSERVABLE from here — said, not inferred from the marker's attach-time stamp.
26
+ */
27
+ import { execFileSync } from "node:child_process";
28
+ import { statSync } from "node:fs";
29
+ import path from "node:path";
30
+ import type { TransportMarker } from "../transports/types.js";
31
+
32
+ export type ProcessFacts = { pid: number; startedAt: number | null; command: string | null; alive: boolean };
33
+ export type PsReader = (pid: number) => ProcessFacts;
34
+ export type StatReader = (p: string) => { mtimeMs: number };
35
+ export type Installed = { module: string; hookPath: string; hookMtime: number | null; buildMtime: number | null };
36
+ export type ServerFacts = { pid: number; startedAt: number; buildMtime: number | null } | null;
37
+
38
+ export type SeatBuild = {
39
+ agentId: string;
40
+ verdict: "current" | "half-restarted" | "stale" | "unknown";
41
+ statement: string;
42
+ server: { observable: boolean; pid: number | null; startedAt: string | null; buildMtime: string | null; current: boolean | null; note: string };
43
+ pusher: {
44
+ pid: number | null;
45
+ alive: boolean | null;
46
+ keyedByAgentArg: boolean | null;
47
+ startedAt: string | null;
48
+ /** The hook file THIS pusher loads, read off its own command line — never a sibling, never the server's tree. */
49
+ hookPath: string | null;
50
+ hookMtime: string | null;
51
+ /** false when the pusher runs from a different tree than the answering server (a dev checkout vs the global install, or two installs). */
52
+ sameTreeAsServer: boolean | null;
53
+ current: boolean | null;
54
+ note: string;
55
+ };
56
+ };
57
+
58
+ const iso = (ms: number | null | undefined) => (typeof ms === "number" && Number.isFinite(ms) ? new Date(ms).toISOString() : null);
59
+
60
+ /** `ps` for one pid: start time and command line. `lstart` is portable across macOS and Linux. */
61
+ export const psReader: PsReader = (pid) => {
62
+ try {
63
+ const out = execFileSync("ps", ["-o", "lstart=,command=", "-p", String(pid)], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
64
+ if (!out) return { pid, startedAt: null, command: null, alive: false };
65
+ // lstart is 24 chars ("Mon Sep 14 14:38:24 2026"), then the command.
66
+ const m = /^(\w{3}\s+\w{3}\s+\d+\s+[\d:]+\s+\d{4})\s+(.*)$/.exec(out);
67
+ const startedAt = m ? Date.parse(m[1]) : NaN;
68
+ return { pid, startedAt: Number.isFinite(startedAt) ? startedAt : null, command: m ? m[2] : out, alive: true };
69
+ } catch {
70
+ return { pid, startedAt: null, command: null, alive: false };
71
+ }
72
+ };
73
+
74
+ /** The installed tree's hook and build stamps, read from the files themselves. */
75
+ export function installedFrom(moduleRoot: string, stat: (p: string) => { mtimeMs: number } = statSync): Installed {
76
+ const hookPath = path.join(moduleRoot, "hooks", "tmux-pusher.mjs");
77
+ const read = (p: string) => {
78
+ try {
79
+ return stat(p).mtimeMs;
80
+ } catch {
81
+ return null;
82
+ }
83
+ };
84
+ return { module: moduleRoot, hookPath, hookMtime: read(hookPath), buildMtime: read(path.join(moduleRoot, "package.json")) };
85
+ }
86
+
87
+ const day = (ms: number) => new Date(ms).toISOString().slice(0, 10);
88
+
89
+ /**
90
+ * One seat's build state: the pusher half from its marker's pid via `ps`, keyed by the
91
+ * `--agent` argument on that process's own command line; the server half from the
92
+ * answering process when the seat IS the answering process, else unobservable.
93
+ */
94
+ /** The hook file a pusher loads, read off its own command line: `node <path>/hooks/tmux-pusher.mjs --agent <id>`. */
95
+ export function hookPathOf(command: string): string | null {
96
+ const m = /(\S*\/hooks\/tmux-pusher\.mjs)(?=\s|$)/.exec(command);
97
+ return m ? m[1] : null;
98
+ }
99
+
100
+ export function seatBuildOf(input: { agentId: string; marker: TransportMarker | undefined; installed: Installed; ps: PsReader; server: ServerFacts; stat?: StatReader }): SeatBuild {
101
+ const { agentId, marker, installed, ps, server } = input;
102
+ const stat = input.stat ?? statSync;
103
+
104
+ // ---- pusher half
105
+ let pusher: SeatBuild["pusher"];
106
+ const empty = { pid: null, alive: null, keyedByAgentArg: null, startedAt: null, hookPath: null, hookMtime: null, sameTreeAsServer: null, current: null };
107
+ if (marker && marker.transport === "herdr") {
108
+ // Phase 5.4 Task 4 — a herdr seat has NO pusher: delivery is in-process by the server,
109
+ // so the seat's build is the server half alone, and this says so instead of reading pid 0.
110
+ pusher = { ...empty, note: `herdr socket transport (pane ${marker.target ?? marker.tmuxTarget ?? "?"}): no pusher process — delivery is in-process by the answering server, so the server half is the whole seat` };
111
+ } else if (!marker || typeof marker.pid !== "number") {
112
+ pusher = { ...empty, note: "no transport marker for this seat — no pusher pid to read" };
113
+ } else {
114
+ const facts = ps(marker.pid);
115
+ if (!facts.alive) {
116
+ pusher = { ...empty, pid: marker.pid, alive: false, note: `the marker names pid ${marker.pid} and no such process is running — the pusher is dead, not stale` };
117
+ } else {
118
+ const cmd = facts.command ?? "";
119
+ const keyed = /tmux-pusher\.mjs/.test(cmd) && new RegExp(`--agent\\s+${agentId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(\\s|$)`).test(cmd);
120
+ if (!keyed) {
121
+ pusher = { ...empty, pid: marker.pid, alive: true, keyedByAgentArg: false, startedAt: iso(facts.startedAt), note: `pid ${marker.pid} is alive but its command line does not say \`tmux-pusher.mjs --agent ${agentId}\` — the marker's pid is not this seat's pusher, so nothing is claimed about it` };
122
+ } else {
123
+ // THE VERY FILE THIS PUSHER LOADS. Its path is on the command line; the mtime is
124
+ // read from that path, not from the answering server's tree — a dev checkout
125
+ // answering for a fleet of global-install pushers would otherwise call every
126
+ // pusher stale against a file none of them ever loaded (measured while building
127
+ // this: 6 of 6 read stale against a worktree's hook, all 6 current against their own).
128
+ const hookPath = hookPathOf(cmd) ?? installed.hookPath;
129
+ const fromOwnCmd = hookPathOf(cmd) !== null;
130
+ let hookMtime: number | null = null;
131
+ try {
132
+ hookMtime = stat(hookPath).mtimeMs;
133
+ } catch {
134
+ hookMtime = null;
135
+ }
136
+ const sameTree = path.resolve(path.dirname(path.dirname(hookPath))) === path.resolve(installed.module);
137
+ if (facts.startedAt === null || hookMtime === null) {
138
+ pusher = { ...empty, pid: marker.pid, alive: true, keyedByAgentArg: true, startedAt: iso(facts.startedAt), hookPath, sameTreeAsServer: sameTree, note: facts.startedAt === null ? "ps gave no start time for the pusher" : `the hook ${hookPath} could not be read` };
139
+ } else {
140
+ const current = facts.startedAt >= hookMtime;
141
+ pusher = {
142
+ pid: marker.pid,
143
+ alive: true,
144
+ keyedByAgentArg: true,
145
+ startedAt: iso(facts.startedAt),
146
+ hookPath,
147
+ hookMtime: iso(hookMtime),
148
+ sameTreeAsServer: sameTree,
149
+ current,
150
+ note:
151
+ (current
152
+ ? `pusher started ${iso(facts.startedAt)}, after its hook ${hookPath} (${iso(hookMtime)}) — it loaded that installed code`
153
+ : `pusher started ${iso(facts.startedAt)}, BEFORE its hook ${hookPath} (${iso(hookMtime)}) — it runs the code it loaded then, and still delivers`) +
154
+ (fromOwnCmd ? "" : " (hook path not on the command line; the answering server's tree was used)") +
155
+ (sameTree ? "" : `; NOTE the pusher's tree differs from the answering server's (${installed.module})`),
156
+ };
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ // ---- server half
163
+ let srv: SeatBuild["server"];
164
+ if (!server) {
165
+ srv = { observable: false, pid: null, startedAt: null, buildMtime: iso(installed.buildMtime), current: null, note: "the server side cannot be keyed to a seat from ps (servers carry no --agent); only the answering process knows its own start time (answeredBy.pid). Ask this seat's own `capabilities`." };
166
+ } else if (server.buildMtime === null) {
167
+ srv = { observable: true, pid: server.pid, startedAt: iso(server.startedAt), buildMtime: null, current: null, note: "the installed package.json could not be read, so the server's build cannot be placed" };
168
+ } else {
169
+ const current = server.startedAt >= server.buildMtime;
170
+ srv = { observable: true, pid: server.pid, startedAt: iso(server.startedAt), buildMtime: iso(server.buildMtime), current, note: current ? `server started ${iso(server.startedAt)}, on the ${day(server.buildMtime)} install` : `server started ${iso(server.startedAt)}, BEFORE the ${day(server.buildMtime)} install` };
171
+ }
172
+
173
+ // ---- the statement
174
+ let verdict: SeatBuild["verdict"];
175
+ let statement: string;
176
+ const p = pusher.current;
177
+ const s = srv.current;
178
+ if (s === true && p === true) {
179
+ verdict = "current";
180
+ statement = `${agentId}: current — server and pusher both started after the ${day(server!.buildMtime!)} install.`;
181
+ } else if (s === false && p === false) {
182
+ verdict = "stale";
183
+ statement = `${agentId}: stale — server and pusher both predate the install; nothing on this seat runs the installed code.`;
184
+ } else if (s === true && p === false) {
185
+ verdict = "half-restarted";
186
+ statement = `${agentId}: HALF-RESTARTED — server on the ${day(server!.buildMtime!)} install, pusher started before it (${pusher.startedAt}). The seat sends through new code and receives through old; it looks healthy. Cycle the pusher: detach_agent, then attach_agent, from this seat.`;
187
+ } else if (s === false && p === true) {
188
+ verdict = "half-restarted";
189
+ statement = `${agentId}: HALF-RESTARTED the other way — pusher on the installed hook, server started before the ${day(server!.buildMtime!)} install. Reload the server (relaunch the MCP client for this seat).`;
190
+ } else if (marker?.transport === "herdr" && s !== null) {
191
+ verdict = s ? "current" : "stale";
192
+ statement = `${agentId}: ${s ? "current" : "stale"} — herdr socket transport, no pusher; the server ${s ? "started after" : "predates"} the ${day(server!.buildMtime!)} install and is the whole seat.`;
193
+ } else if (s === null && p !== null) {
194
+ verdict = "unknown";
195
+ statement = `${agentId}: pusher ${p ? "current" : "STALE — started before the installed hook"}; server UNOBSERVABLE from here (${srv.note}).`;
196
+ } else {
197
+ verdict = "unknown";
198
+ statement = `${agentId}: unknown — ${pusher.note}${srv.observable ? "" : `; ${srv.note}`}`;
199
+ }
200
+ return { agentId, verdict, statement, server: srv, pusher };
201
+ }
@@ -86,6 +86,28 @@ export type AgentEntry = {
86
86
  // OTHER agents' context on every multi-line send. That is why it is stamped,
87
87
  // counted, and reviewed rather than self-service.
88
88
  proseOnly?: { since: number; reason?: string };
89
+
90
+ /*
91
+ * ⛔⛆ THE ANSWERING PROCESS, PUBLISHED — `⟨q-cec42e20⟩`. ADDITIVE AND OPTIONAL:
92
+ * absent on every pre-existing entry, and no reader that ignores unknown keys changes
93
+ * behaviour.
94
+ *
95
+ * Two seats ran the same verb, got `153` and `138`, and both servers honestly reported
96
+ * `versionLabel 0.26.20`. Nothing detected it — each seat had to VOLUNTEER it.
97
+ *
98
+ * ⭐ WHY THESE TWO FIELDS AND NOT A VERSION: a server loads its code once, at spawn,
99
+ * so START TIME against the installed build is the axis that separates two processes
100
+ * running different code. A label cannot: it AGREED while the behaviour differed, and
101
+ * `serverBuildMtime` cannot either — it is stamped at ATTACH and read IDENTICAL across
102
+ * all six live transports while the spread was live.
103
+ *
104
+ * ⚠ AND IT IS THE SERVER, NEVER THE PUSHER. `transports/<agent>.json` already carries a
105
+ * pid and it is the pusher's — a different process, on different code, which is the
106
+ * distinction this fleet paid for twice on 2026-09-12.
107
+ */
108
+ serverPid?: number;
109
+ serverStartedAt?: number;
110
+ serverModule?: string;
89
111
  };
90
112
 
91
113
  /**