instar 1.3.574 → 1.3.576

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.
@@ -21,12 +21,6 @@ export declare const WS2_SEND_WIRED_STORES: ReadonlyArray<string>;
21
21
  /**
22
22
  * Stores registered for RECEIVE but whose SEND wiring is a KNOWN, enumerated
23
23
  * follow-up — NOT a silent omission. Each is here for a stated reason:
24
- * - userRegistry: fully seamed manager (emitPut + emitDelete); its canonical write
25
- * instance lives in the AgentServer, so it needs the emitter plumbed there before it
26
- * can be wired (WS2-SEND-2b).
27
- * - topicOperator: seamed put-only (a topic rebinds, never unbinds — no emitDelete by
28
- * construction); its authoritative writer is the AgentServer's TopicOperatorStore, so it
29
- * needs the emitter plumbed there before it can be wired (WS2-SEND-2b).
30
24
  * - preferences: NO manager emit seam yet — it rode the deprecated `preferences-sync`
31
25
  * verb; needs a manager emit hook before it can be wired (WS2-SEND-3).
32
26
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ws2SendWiring.d.ts","sourceRoot":"","sources":["../../src/core/ws2SendWiring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;+CAE+C;AAC/C,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAKtD,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,EAAE,aAAa,CAAC,MAAM,CAIxD,CAAC;AAEH,uDAAuD;AACvD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,cAAc,CAAC;AAEjE,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAI1D;AAED,kFAAkF;AAClF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,6FAA6F;IAC7F,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,EAAE,EAAE,OAAO,CAAC;CACb;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,kBAAkB,CAW9F"}
1
+ {"version":3,"file":"ws2SendWiring.d.ts","sourceRoot":"","sources":["../../src/core/ws2SendWiring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;+CAE+C;AAC/C,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAOtD,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,aAAa,CAAC,MAAM,CAExD,CAAC;AAEH,uDAAuD;AACvD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,cAAc,CAAC;AAEjE,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAI1D;AAED,kFAAkF;AAClF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,6FAA6F;IAC7F,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,EAAE,EAAE,OAAO,CAAC;CACb;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,kBAAkB,CAW9F"}
@@ -22,22 +22,16 @@ export const WS2_SEND_WIRED_STORES = Object.freeze([
22
22
  'relationships',
23
23
  'knowledge',
24
24
  'evolutionActions',
25
+ 'topicOperator',
26
+ 'userRegistry',
25
27
  ]);
26
28
  /**
27
29
  * Stores registered for RECEIVE but whose SEND wiring is a KNOWN, enumerated
28
30
  * follow-up — NOT a silent omission. Each is here for a stated reason:
29
- * - userRegistry: fully seamed manager (emitPut + emitDelete); its canonical write
30
- * instance lives in the AgentServer, so it needs the emitter plumbed there before it
31
- * can be wired (WS2-SEND-2b).
32
- * - topicOperator: seamed put-only (a topic rebinds, never unbinds — no emitDelete by
33
- * construction); its authoritative writer is the AgentServer's TopicOperatorStore, so it
34
- * needs the emitter plumbed there before it can be wired (WS2-SEND-2b).
35
31
  * - preferences: NO manager emit seam yet — it rode the deprecated `preferences-sync`
36
32
  * verb; needs a manager emit hook before it can be wired (WS2-SEND-3).
37
33
  */
38
34
  export const WS2_SEND_PENDING_STORES = Object.freeze([
39
- 'userRegistry',
40
- 'topicOperator',
41
35
  'preferences',
42
36
  ]);
43
37
  /** Classify a registered store's send-wiring status. */
@@ -1 +1 @@
1
- {"version":3,"file":"ws2SendWiring.js","sourceRoot":"","sources":["../../src/core/ws2SendWiring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;+CAE+C;AAC/C,MAAM,CAAC,MAAM,qBAAqB,GAA0B,MAAM,CAAC,MAAM,CAAC;IACxE,WAAW;IACX,eAAe;IACf,WAAW;IACX,kBAAkB;CACnB,CAAC,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAA0B,MAAM,CAAC,MAAM,CAAC;IAC1E,cAAc;IACd,eAAe;IACf,aAAa;CACd,CAAC,CAAC;AAKH,wDAAwD;AACxD,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,IAAI,qBAAqB,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAC1D,IAAI,uBAAuB,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9D,OAAO,cAAc,CAAC;AACxB,CAAC;AAWD;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,gBAAuC;IACxE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,KAAK,MAAM,KAAK,IAAI,gBAAgB,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,MAAM,KAAK,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;aACrC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;YAC9C,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;AACzE,CAAC"}
1
+ {"version":3,"file":"ws2SendWiring.js","sourceRoot":"","sources":["../../src/core/ws2SendWiring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;+CAE+C;AAC/C,MAAM,CAAC,MAAM,qBAAqB,GAA0B,MAAM,CAAC,MAAM,CAAC;IACxE,WAAW;IACX,eAAe;IACf,WAAW;IACX,kBAAkB;IAClB,eAAe;IACf,cAAc;CACf,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAA0B,MAAM,CAAC,MAAM,CAAC;IAC1E,aAAa;CACd,CAAC,CAAC;AAKH,wDAAwD;AACxD,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,IAAI,qBAAqB,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAC1D,IAAI,uBAAuB,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9D,OAAO,cAAc,CAAC;AACxB,CAAC;AAWD;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,gBAAuC;IACxE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,KAAK,MAAM,KAAK,IAAI,gBAAgB,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,MAAM,KAAK,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;aACrC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;YAC9C,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;AACzE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.574",
3
+ "version": "1.3.576",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-06-15T18:18:56.240Z",
5
- "instarVersion": "1.3.574",
4
+ "generatedAt": "2026-06-15T19:11:56.413Z",
5
+ "instarVersion": "1.3.576",
6
6
  "entryCount": 201,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,44 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The `topicOperator` store is now wired into the WS2 send-side (a PII kind; WS2-SEND-2b).
9
+ Its authoritative writer is the AgentServer's own `TopicOperatorStore`, so the generic
10
+ emitter (#1168) is attached to `server.getTopicOperatorStore()` right after the AgentServer
11
+ is constructed, and `topicOperator` flips PENDING→WIRED in the send-wiring ratchet. The
12
+ union reader + disclosure-minimized projection already shipped (WS2.6). PUT-ONLY by
13
+ construction — a topic rebinds, never unbinds. Dark by default
14
+ (`multiMachine.stateSync.topicOperator`). No new route/verb/config-default/migration.
15
+
16
+ ## What to Tell Your User
17
+
18
+ - **Who-runs-which-topic now travels across machines**: "If you run me on more than one
19
+ machine, the verified operator I bound for a topic on one machine is visible as advisory
20
+ context on the others — so a topic's operator isn't re-learned from scratch per machine.
21
+ Only the platform-verified identity crosses (the verified user id + display name) — a name
22
+ that merely appears in a message can never become an operator. Crucially, a binding that
23
+ arrives from another machine is treated as a HINT, never as the final word on 'who is my
24
+ operator here' — only a binding I make locally from an authenticated sender is
25
+ authoritative. It stays off until you ask me to turn on multi-machine sync." ⚗️
26
+ Experimental — ships dark.
27
+
28
+ ## Summary of New Capabilities
29
+
30
+ | Capability | How to Use |
31
+ |-----------|-----------|
32
+ | Cross-machine visibility of verified topic-operator bindings (advisory) | Automatic once `multiMachine.stateSync.topicOperator` is enabled (off by default) |
33
+ | Put-only by construction — a rebind supersedes by HLC; a topic never unbinds | Automatic |
34
+ | Know-Your-Principal preserved — a replicated record is never authoritative for inbound resolution | Automatic (local authenticated bind always wins) |
35
+
36
+ ## Evidence
37
+
38
+ Verified by a two-instance in-process E2E
39
+ (`tests/e2e/ws2-topicoperator-cross-instance.test.ts`): a `setOperator` bind on instance A
40
+ is read back on B through the bypass-proof union reader as a foreign-origin record keyed on
41
+ (topicId + verified uid), carrying only the verified uid + lowercased names + platform; and
42
+ an idempotent re-bind with a later `boundAt` re-replicates the latest record (put-only —
43
+ there is no delete path). `tsc --noEmit` clean; the new e2e (2) passes; the ws2-send-wiring
44
+ integration ratchet (4) accepts the PENDING→WIRED move.
@@ -0,0 +1,52 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The `userRegistry` store (a PII kind) is now wired into the WS2 send-side. Unlike the
9
+ seamed memory stores, there is no single canonical `UserManager` — telegram (send-only +
10
+ normal mode) and slack each construct their own long-lived instance. A shared
11
+ `attachUserReplication` helper in `server.ts` wires the generic emitter (#1168) to each at
12
+ its construction site, and `userRegistry` flips PENDING→WIRED in the ratchet (leaving only
13
+ `preferences`). Channel-keyed identity — the local userId never crosses. Dark by default
14
+ (`multiMachine.stateSync.userRegistry`). No new route/verb/config-default/migration.
15
+
16
+ ## What to Tell Your User
17
+
18
+ - **The people I know now travel across machines**: "If you run me on more than one
19
+ machine, a user I learn about on one (from telegram or slack) now shows up in my user
20
+ registry on the others — one shared set of people instead of one per machine. Only the
21
+ profile crosses (name, channels, permissions) — never the local id I use internally, and
22
+ identity is matched by a person's channel set so the same person collapses to one record
23
+ across machines. Importantly, a user record that arrives from another machine is a HINT
24
+ only — it is NEVER how I decide who an incoming message is actually from (that stays a
25
+ local, verified decision). It stays off until you ask me to turn on multi-machine sync."
26
+ ⚗️ Experimental — ships dark.
27
+
28
+ ## Summary of New Capabilities
29
+
30
+ | Capability | How to Use |
31
+ |-----------|-----------|
32
+ | Cross-machine replication of the user registry (profile metadata, channel-keyed) | Automatic once `multiMachine.stateSync.userRegistry` is enabled (off by default) |
33
+ | Same person (same channel set) on two machines collapses to one record | Automatic (read path) |
34
+ | Removing a user propagates a channel-keyed tombstone (no resurrection) | Automatic (removeUser path) |
35
+
36
+ ## Evidence
37
+
38
+ Verified by a two-instance in-process E2E
39
+ (`tests/e2e/ws2-userregistry-cross-instance.test.ts`): a user added on instance A is read
40
+ back on B through the bypass-proof union reader as a foreign-origin record (channel-keyed,
41
+ local userId confirmed ABSENT); the same user (same channel set) on both machines collapses
42
+ to one record key across origins; and a removeUser on A replicates as a channel-keyed
43
+ tombstone so B resolves the key to "no record". `tsc --noEmit` clean; the new e2e (3) passes;
44
+ the ws2-send-wiring integration ratchet (4) accepts the PENDING→WIRED move.
45
+
46
+ ## Known capture-scope (honest)
47
+
48
+ The in-process emitter captures the dominant server-process user-creation paths (telegram
49
+ both modes + slack inbound). Two secondary paths are NOT captured by design and are
50
+ documented in the side-effects review: the Slack org-permission admin route (a per-request
51
+ UserManager) and the `instar user add` CLI (a separate process). A single canonical-instance
52
+ funnel would close both and is a reasonable future refactor.
@@ -0,0 +1,45 @@
1
+ # Side-Effects Review — WS2-SEND-2b: topicOperator send-side replication
2
+
3
+ **Version / slug:** `ws2-send-2b-topicoperator`
4
+ **Date:** `2026-06-15`
5
+ **Author:** `Instar Agent (echo)`
6
+ **Second-pass reviewer:** `not required` (no block/allow/lifecycle authority — additive, dark-gated data-replication wiring; see §4)
7
+
8
+ ## Summary of the change
9
+
10
+ Extends WS2 send-side emission to the `topicOperator` store (a PII kind; WS2-SEND-2b). Unlike the seamed memory stores, topicOperator's authoritative writer is the AgentServer's OWN `TopicOperatorStore` (constructed internally at `AgentServer.ts:1092`, bound from the authenticated sender via `setOperator`). server.ts has no canonical instance of its own, so the emitter is attached AFTER the AgentServer exists, in server.ts right after `_agentServerRef = server`, via `server.getTopicOperatorStore()?.setOperatorReplicationEmitter(...)`. `ws2SendWiring.ts` moves `topicOperator` PENDING→WIRED. The union reader + projection already shipped (WS2.6). New e2e round-trip.
11
+
12
+ **PUT-ONLY by construction** — a topic-operator binding is rebound, never erased (the manager has no emitDelete path; the receive side resolves the latest binding by HLC). Wiring put-only is the COMPLETE correct behavior, not a deferred gap.
13
+
14
+ ## Decision-point inventory
15
+
16
+ - `ReplicatedRecordEmitter.emit` dark gate (`stateSync.topicOperator.enabled`) — **pass-through** — pre-existing; `topicOperator` becomes a registered emit target. Default false ⇒ no-op.
17
+ - `TopicOperatorStore.setOperator` emit funnel — **pass-through** — already fires emitPut on a real bind/rebind; this attaches a real emitter.
18
+ - `ws2SendWiring` ratchet — **modify** — `topicOperator` reclassified PENDING→WIRED.
19
+
20
+ ---
21
+
22
+ ## 1. Over-block
23
+ No block/allow surface. The emitter never rejects a bind; null recordKey / null projection / over-cap is a counted no-op and the local bind always succeeds.
24
+
25
+ ## 2. Under-block
26
+ Not applicable (no block surface). PUT-ONLY is the correct, complete design: a topic rebinds (a new put supersedes by HLC), never unbinds, so there is no emitDelete to wire. A `buildTopicOperatorTombstoneData` helper exists for symmetry but no manager event fires it — by construction, not omission.
27
+
28
+ ## 3. Level-of-abstraction fit
29
+ Correct layer. The attach point is at the server-composition layer (where the AgentServer and its store exist), which is the only place the authoritative TopicOperatorStore is reachable. The projection lives in `TopicOperatorReplicatedStore` beside the receive-side schema.
30
+
31
+ ## 4. Signal vs authority compliance
32
+ Compliant. No blocking authority. The emitter is additive/best-effort (the funnel swallows + counts faults; a bind can never fail because replication did). **Know Your Principal (REQ-M14):** only the platform-verified `uid` + lowercased display names are emitted — a content name can NEVER become an operator. A replicated topic-operator record is UNTRUSTED peer data and is NEVER the authoritative answer to "who is my verified operator of this topic?" — only the LOCAL bind from an authenticated sender is authoritative. The union read is advisory HIGH-tier. Per `docs/signal-vs-authority.md`.
33
+
34
+ ## 5. Interactions
35
+ - Uses server.ts's single `replicatedRecordEmitter` (in scope at the attach point, constructed earlier). Distinct store key/kind from the other WS2 stores. No double-fire (rides the single setOperator funnel).
36
+ - Touches `server.ts` + `ws2SendWiring.ts` (the shared WS2-SEND files) → serialized on top of merged evolutionActions; no parallel WS2-SEND PRs.
37
+
38
+ ## 6. External surfaces
39
+ Dark by default (`multiMachine.stateSync.topicOperator`). Off ⇒ byte-identical single-machine behavior. On (multi-machine, opt-in): crosses the sha-keyed (topicId + verified uid) projection — platform, uid, lowercased names, boundAt. Same at-rest honesty as relationships (transit encrypted; at-rest plaintext per machine). A received record is quoted `<replicated-untrusted-data>`, advisory only — never adopted as the operator.
40
+
41
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
42
+ **Replicated (put-only).** Path: `TopicOperatorStore.setOperator → emit → CoherenceJournal.emitReplicatedRecord → peer serve/apply → ReplicatedPeerStreamReader → topicOperatorUnionReader`. Identity = sha(topicId + verified uid). A rebind re-emits the latest record (HLC-ordered); there is no delete (a topic never unbinds). Verified end-to-end by the new e2e (put round-trip + idempotent rebind replicates the latest boundAt).
43
+
44
+ ## 8. Rollback cost
45
+ Trivial. Dark by default. Back-out = revert the server.ts + ws2SendWiring.ts change or set the flag false (instant, no migration). Receive-side + envelope schema already shipped, unaffected.
@@ -0,0 +1,48 @@
1
+ # Side-Effects Review — WS2-SEND-2b: userRegistry send-side replication
2
+
3
+ **Version / slug:** `ws2-send-2b-userregistry`
4
+ **Date:** `2026-06-15`
5
+ **Author:** `Instar Agent (echo)`
6
+ **Second-pass reviewer:** `not required` (no block/allow/lifecycle authority — additive, dark-gated data-replication wiring; see §4)
7
+
8
+ ## Summary of the change
9
+
10
+ Extends WS2 send-side emission to the `userRegistry` store (the SECOND PII kind; WS2-SEND-2b). Unlike the seamed memory stores, `userRegistry` has NO single canonical UserManager — telegram (send-only mode + normal mode) and slack each construct their OWN long-lived `UserManager` against the same `users.json`. So a single shared attacher (`attachUserReplication`, defined in `src/commands/server.ts` right after the emitter is constructed) wires the journal-backed emitter to each long-lived instance at its construction site (`userManagerSendOnly`, `userManager`, `slackUserManager`). `ws2SendWiring.ts` moves `userRegistry` PENDING→WIRED (leaving only `preferences`). The union reader + projection already shipped (WS2.6). New e2e round-trip.
11
+
12
+ Channel-keyed identity — the local `userId` NEVER crosses. `upsertUser→persistUsers` fires emitPut for every surviving user; `removeUser` fires the channel-keyed emitDelete tombstone.
13
+
14
+ ## Decision-point inventory
15
+
16
+ - `ReplicatedRecordEmitter.emit` dark gate (`stateSync.userRegistry.enabled`) — **pass-through** — pre-existing; `userRegistry` becomes a registered emit target. Default false ⇒ no-op.
17
+ - `UserManager.persistUsers` / `removeUser` emit funnels — **pass-through** — already fire; this attaches a real emitter at each long-lived instance.
18
+ - `ws2SendWiring` ratchet — **modify** — `userRegistry` reclassified PENDING→WIRED.
19
+
20
+ ---
21
+
22
+ ## 1. Over-block
23
+ No block/allow surface. The emitter never rejects a user write; null recordKey / null projection / over-cap is a counted no-op and the local write always succeeds.
24
+
25
+ ## 2. Under-block
26
+ Not applicable (no block surface). **Honest capture-scope note** (the load-bearing caveat): the in-process emitter fires on the SERVER-PROCESS user-write paths — telegram (both modes) + slack inbound registration, which are the dominant user-creation paths. TWO secondary paths are NOT covered, by design, and are stated rather than silently dropped:
27
+ 1. The Slack org-permission **admin** route (`buildSlackRegistry` in routes.ts) constructs a per-request UserManager; reaching the emitter there needs RouteContext plumbing disproportionate to a secondary admin flow.
28
+ 2. The `instar user add` **CLI** runs in a SEPARATE PROCESS, so it can never fire the server's in-process emitter; the send-side snapshot reads the journal's own entries, not `users.json`, so a CLI-only user is a known gap.
29
+ This matches the accepted in-process capture scope of relationships/knowledge. A single write funnel (one canonical UserManager) would close both gaps and is a reasonable future refactor; it is out of scope for "identical wiring."
30
+
31
+ ## 3. Level-of-abstraction fit
32
+ Correct layer. A shared attacher at the construction sites is the minimal change that captures the multiple long-lived instances without a refactor. The projection lives in `UserRegistryReplicatedStore` beside the receive-side schema.
33
+
34
+ ## 4. Signal vs authority compliance
35
+ Compliant. No blocking authority. The emitter is additive/best-effort (the funnel swallows + counts faults; a user write can never fail because replication did). **REQ-M14 (Know Your Principal):** a replicated user record is UNTRUSTED peer data and is NEVER the authoritative answer to "who is this inbound sender?" — inbound-principal resolution stays LOCAL-ONLY (the local channel index always wins). The union read is advisory HIGH-tier. Per `docs/signal-vs-authority.md`.
36
+
37
+ ## 5. Interactions
38
+ - Uses server.ts's single `replicatedRecordEmitter` via the shared `attachUserReplication` helper. Distinct store key/kind from the other WS2 stores. No double-fire (each instance rides its own persistUsers funnel; the same person on two instances collapses to one channel-keyed recordKey).
39
+ - Touches `server.ts` + `ws2SendWiring.ts` (the shared WS2-SEND files) → serialized on top of merged topicOperator; no parallel WS2-SEND PRs.
40
+
41
+ ## 6. External surfaces
42
+ Dark by default (`multiMachine.stateSync.userRegistry`). Off ⇒ byte-identical single-machine behavior. On (multi-machine, opt-in): crosses the channel-keyed profile projection (name, channels, permissions) — never the local userId. Same at-rest honesty as relationships (transit encrypted; at-rest plaintext per machine). A received record is quoted `<replicated-untrusted-data>`, advisory only.
43
+
44
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
45
+ **Replicated.** Path: `upsertUser/removeUser → persistUsers → emit → CoherenceJournal.emitReplicatedRecord → peer serve/apply → ReplicatedPeerStreamReader → userRegistryUnionReader`. Identity = channel set (the same person on two machines collapses to one recordKey). removeUser propagates a channel-keyed tombstone (no resurrection). Verified end-to-end by the new e2e (put round-trip + channel-set collapse + removeUser tombstone resolves to no-record).
46
+
47
+ ## 8. Rollback cost
48
+ Trivial. Dark by default. Back-out = revert the server.ts + ws2SendWiring.ts change or set the flag false (instant, no migration). Receive-side + envelope schema already shipped, unaffected.