instar 1.3.571 → 1.3.573

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,7 +21,7 @@ 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
- * - relationships / knowledge / evolutionActions / userRegistry: fully seamed
24
+ * - evolutionActions / userRegistry: fully seamed
25
25
  * managers (emitPut + emitDelete); table-row wiring onto the same emitter (WS2-SEND-2).
26
26
  * - topicOperator: seamed put-only (no emitDelete in its manager interface yet) (WS2-SEND-3).
27
27
  * - preferences: NO manager emit seam yet — it rode the deprecated `preferences-sync`
@@ -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,CAEtD,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,EAAE,aAAa,CAAC,MAAM,CAOxD,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,CAItD,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,EAAE,aAAa,CAAC,MAAM,CAKxD,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"}
@@ -19,19 +19,19 @@
19
19
  * follow on the SAME emitter (WS2-SEND-2). */
20
20
  export const WS2_SEND_WIRED_STORES = Object.freeze([
21
21
  'learnings',
22
+ 'relationships',
23
+ 'knowledge',
22
24
  ]);
23
25
  /**
24
26
  * Stores registered for RECEIVE but whose SEND wiring is a KNOWN, enumerated
25
27
  * follow-up — NOT a silent omission. Each is here for a stated reason:
26
- * - relationships / knowledge / evolutionActions / userRegistry: fully seamed
28
+ * - evolutionActions / userRegistry: fully seamed
27
29
  * managers (emitPut + emitDelete); table-row wiring onto the same emitter (WS2-SEND-2).
28
30
  * - topicOperator: seamed put-only (no emitDelete in its manager interface yet) (WS2-SEND-3).
29
31
  * - preferences: NO manager emit seam yet — it rode the deprecated `preferences-sync`
30
32
  * verb; needs a manager emit hook before it can be wired (WS2-SEND-3).
31
33
  */
32
34
  export const WS2_SEND_PENDING_STORES = Object.freeze([
33
- 'relationships',
34
- 'knowledge',
35
35
  'evolutionActions',
36
36
  'userRegistry',
37
37
  'topicOperator',
@@ -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;CACZ,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAA0B,MAAM,CAAC,MAAM,CAAC;IAC1E,eAAe;IACf,WAAW;IACX,kBAAkB;IAClB,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;CACZ,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAA0B,MAAM,CAAC,MAAM,CAAC;IAC1E,kBAAkB;IAClB,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.571",
3
+ "version": "1.3.573",
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-15T09:28:36.949Z",
5
- "instarVersion": "1.3.571",
4
+ "generatedAt": "2026-06-15T17:50:05.433Z",
5
+ "instarVersion": "1.3.573",
6
6
  "entryCount": 201,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,55 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The `relationships` memory store is now wired into the WS2 send-side, the first of the
9
+ four "table-row" stores the WS2-SEND-SIDE-EMISSION-SPEC enumerated as the WS2-SEND-2
10
+ follow-on. #1168 proved the generic `ReplicatedRecordEmitter` machinery end-to-end for
11
+ `learnings`; this change attaches that same emitter to the `RelationshipManager`'s
12
+ existing write hooks (the manager already fired `emitPut` on every saved person and
13
+ `emitDelete` on erase/merge — the emitter was simply never attached), and flips
14
+ `relationships` from PENDING to WIRED in the send-wiring ratchet (`ws2SendWiring.ts`).
15
+
16
+ No new design: the disclosure-minimized projection (`buildRelationshipRecordData` /
17
+ `buildRelationshipTombstoneData`), the channel-set identity surface, the receive-side
18
+ schema, and the no-clobber union read all already shipped with the WS2.3 relationships
19
+ work. This is the send attachment + its end-to-end proof. Everything stays behind the
20
+ per-store dark gate (`multiMachine.stateSync.relationships`, off by default — the
21
+ development-agent gate flips it live on a dev agent only). Records ride the existing
22
+ journal-sync tail and pull — no new HTTP route or mesh verb.
23
+
24
+ ## What to Tell Your User
25
+
26
+ - **Cross-machine relationships now actually cross**: "If you run me on more than one
27
+ machine, a person I know on one is now known on the others — one shared relationship
28
+ graph instead of one per machine. Only a disclosure-minimized, channel-keyed
29
+ projection crosses (never the raw record or the local id), a copy from another machine
30
+ is always a hint and never the authority on who is messaging you, and an erased person
31
+ stays erased everywhere via a tombstone. It stays off until you ask me to turn on
32
+ multi-machine relationship sync." ⚗️ Experimental — ships dark; the learnings slice is
33
+ the proven path and the remaining stores follow on the same wiring.
34
+
35
+ ## Summary of New Capabilities
36
+
37
+ | Capability | How to Use |
38
+ |-----------|-----------|
39
+ | Cross-machine replication of known people (relationships) | Automatic once `multiMachine.stateSync.relationships` is enabled (off by default) |
40
+ | Channel-keyed identity — same person on two machines collapses to one record | Automatic (read path) |
41
+ | Erasure propagates as a channel-keyed tombstone (stays erased on offline-then-rejoining peers) | Automatic (delete path) |
42
+
43
+ ## Evidence
44
+
45
+ Verified by a two-instance in-process E2E
46
+ (`tests/e2e/ws2-relationships-cross-instance.test.ts`): a person recorded on instance A
47
+ (`findOrCreate`), shipped over the real journal serve/apply path, is read back on
48
+ instance B through the bypass-proof union reader as a foreign-origin record — and the
49
+ local UUID is confirmed absent from the projection; a `delete` on A replicates as a
50
+ channel-keyed tombstone and B's union read resolves the key to "no record"; the same
51
+ person (same channel set) recorded on both machines collapses to one record key across
52
+ origins. Before the attachment B's union read returned null; after, it returns A's
53
+ record. `tsc --noEmit` clean; the new e2e (3) + the learnings e2e (3) pass; the
54
+ ws2-send-wiring integration ratchet (4) accepts the PENDING→WIRED move; the existing
55
+ `relationship-replication-emit` seam unit tests (6) stay green.
@@ -0,0 +1,47 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The `knowledge` memory store is now wired into the WS2 send-side — the second of the
9
+ WS2-SEND-2 table-row stores (after `relationships`). The generic emitter from #1168 is
10
+ attached to `KnowledgeManager`'s existing ingest/remove hooks (the manager already fired
11
+ `emitPut`/`emitDelete`; the emitter was a `void` placeholder), and `knowledge` flips
12
+ PENDING→WIRED in the send-wiring ratchet. The knowledge union reader and the
13
+ disclosure-minimized projection already shipped (WS2.4); this is the send attachment +
14
+ its end-to-end proof. Dark by default (`multiMachine.stateSync.knowledge`). No new
15
+ route/verb/config-default/migration. Only catalog METADATA crosses — never the markdown
16
+ body, never the local id/filePath.
17
+
18
+ ## What to Tell Your User
19
+
20
+ - **Cross-machine knowledge catalog now actually crosses**: "If you run me on more than
21
+ one machine, a source I ingest on one (an article, transcript, or doc) now shows up in
22
+ the catalog on the others — one shared knowledge index instead of one per machine. Only
23
+ the catalog entry crosses (title, link, type, tags, summary) — never the full document
24
+ text and never the local file path; the other machine learns the source exists and can
25
+ re-fetch it itself. It stays off until you ask me to turn on multi-machine knowledge
26
+ sync." ⚗️ Experimental — ships dark; metadata-only by design (full-content sync is a
27
+ separate tracked stage).
28
+
29
+ ## Summary of New Capabilities
30
+
31
+ | Capability | How to Use |
32
+ |-----------|-----------|
33
+ | Cross-machine replication of the knowledge catalog (metadata only) | Automatic once `multiMachine.stateSync.knowledge` is enabled (off by default) |
34
+ | Content-fingerprint identity — the same source on two machines collapses to one record | Automatic (read path) |
35
+ | Removal propagates as a fingerprint-keyed tombstone (no resurrection of a removed source) | Automatic (remove path) |
36
+
37
+ ## Evidence
38
+
39
+ Verified by a two-instance in-process E2E
40
+ (`tests/e2e/ws2-knowledge-cross-instance.test.ts`): a source ingested on instance A is
41
+ read back on B through the bypass-proof union reader as a foreign-origin record — and the
42
+ markdown body, local `id`, and `filePath` are confirmed ABSENT from the projection
43
+ (metadata only); a `remove` on A replicates as a fingerprint-keyed tombstone and B's
44
+ union read resolves the key to "no record" (no resurrection); the same source (same
45
+ url+type) ingested on both machines collapses to one record key across origins. `tsc
46
+ --noEmit` clean; the new e2e (3) + relationships e2e (3) pass; the ws2-send-wiring
47
+ integration ratchet (4) accepts the PENDING→WIRED move.
@@ -0,0 +1,51 @@
1
+ # Side-Effects Review — WS2-SEND-2: knowledge send-side replication
2
+
3
+ **Version / slug:** `ws2-send-2-knowledge`
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 `knowledge` store (the second WS2-SEND-2 table-row store after `relationships`). Mirrors the proven pattern: `src/commands/server.ts` imports `buildKnowledgeRecordData` + `buildKnowledgeTombstoneData` and attaches the generic `ReplicatedRecordEmitter` to `KnowledgeManager.setKnowledgeReplicationEmitter` (replacing the prior `void` placeholder), gated `if (replicatedRecordEmitter)`; `src/core/ws2SendWiring.ts` moves `knowledge` PENDING→WIRED; a new e2e round-trip `tests/e2e/ws2-knowledge-cross-instance.test.ts`. The knowledge union reader already existed (constructed @8566). No decision-point surface added beyond the pre-existing dark gate.
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `ReplicatedRecordEmitter.emit` dark gate (`stateSync.knowledge.enabled`) — **pass-through** — pre-existing; `knowledge` is now a registered emit target. Default false ⇒ no-op.
15
+ - `KnowledgeManager.ingest`/`remove` emit funnel — **pass-through** — the manager already fires emitPut@165 / emitDelete@204; this attaches a real emitter where there was a no-op.
16
+ - `ws2SendWiring` ratchet — **modify** — `knowledge` reclassified PENDING→WIRED.
17
+
18
+ ---
19
+
20
+ ## 1. Over-block
21
+
22
+ No block/allow surface — over-block not applicable. The emitter never rejects an ingest/remove; a null recordKey / null projection / over-cap source is a counted no-op and the local catalog write always succeeds.
23
+
24
+ ## 2. Under-block
25
+
26
+ Not applicable (no block surface). A source with an empty identity anchor (no url AND no title) has no cross-machine fingerprint and is intentionally not replicated (by design) — local-only.
27
+
28
+ ## 3. Level-of-abstraction fit
29
+
30
+ Correct layer. Table-row registration onto the generic #1168 substrate, exactly as the spec's WS2-SEND-2 prescribes. The disclosure-minimized projection lives in `KnowledgeReplicatedStore` alongside the receive-side schema.
31
+
32
+ ## 4. Signal vs authority compliance
33
+
34
+ Compliant. No blocking authority. The emitter is additive/best-effort: a builder/journal failure is swallowed + counted, never propagated, so a knowledge ingest can never fail because replication did. The union read is advisory (HIGH tier append-both-and-flag). Per `docs/signal-vs-authority.md`, a replicator with no gate authority.
35
+
36
+ ## 5. Interactions
37
+
38
+ - Shares `server.ts`'s single `replicatedRecordEmitter` + the existing `knowledgeUnionReader`. No double-fire: emitPut rides the single `ingest()` path, emitDelete the single `remove()` path. Distinct store key/kind from learnings + relationships.
39
+ - Touches the same `server.ts` + `ws2SendWiring.ts` as the other WS2-SEND stores → serialized (built on top of the merged #1169 relationships change; no parallel WS2-SEND PRs).
40
+
41
+ ## 6. External surfaces
42
+
43
+ Dark by default (`multiMachine.stateSync.knowledge.enabled`). Off ⇒ byte-identical single-machine behavior. On (multi-machine, opt-in): only the catalog METADATA crosses (title, url, type, tags, summary, wordCount) — NEVER the markdown file BODY and NEVER the local id/filePath (fork #2). The peer LEARNS the source exists and may re-ingest locally; full-content sync is a separate tracked rollout stage. A received record is quoted `<replicated-untrusted-data>`, advisory reference only.
44
+
45
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
46
+
47
+ **Replicated.** Path: `KnowledgeManager.ingest/remove` → `ReplicatedRecordEmitter.emit` → `CoherenceJournal.emitReplicatedRecord` → peer serve/apply → `ReplicatedPeerStreamReader.loadOriginRecords` → `knowledgeUnionReader` (no-clobber union, conflict-flagged). Identity = content fingerprint (url||title + type), so the same source on two machines collapses to one recordKey. remove() propagates a fingerprint-keyed tombstone (a later `delete` hlc wins over an earlier `put` — no resurrection). No user-facing notice surface; no topic-transfer state; no generated URLs.
48
+
49
+ ## 8. Rollback cost
50
+
51
+ Trivial. Dark by default; affects only agents that enable `stateSync.knowledge`. Back-out = revert the three-file change or set the flag false (instant, no migration). Receive-side + envelope schema already shipped, unaffected.
@@ -0,0 +1,52 @@
1
+ # Side-Effects Review — WS2-SEND-2: relationships send-side replication
2
+
3
+ **Version / slug:** `ws2-send-2-relationships`
4
+ **Date:** `2026-06-15`
5
+ **Author:** `Instar Agent (echo)`
6
+ **Second-pass reviewer:** `not required` (no block/allow/lifecycle authority — pure additive, dark-gated data-replication wiring; see §4)
7
+
8
+ ## Summary of the change
9
+
10
+ Extends the WS2 send-side emission (proven end-to-end for `learnings` in #1168) to the `relationships` store — the first of the four "table-row" stores the WS2-SEND-SIDE-EMISSION-SPEC enumerates as WS2-SEND-2. Three edits, all mirroring the learnings slice: (1) `src/commands/server.ts` imports `buildRelationshipRecordData` + `buildRelationshipTombstoneData` and attaches the journal-backed `ReplicatedRecordEmitter` to `RelationshipManager.setReplicationEmitter` (emitPut/emitDelete → build\*RecordData), gated `if (replicatedRecordEmitter && relationships)`; (2) `src/core/ws2SendWiring.ts` moves `relationships` from `WS2_SEND_PENDING_STORES` to `WS2_SEND_WIRED_STORES`; (3) a new e2e round-trip test `tests/e2e/ws2-relationships-cross-instance.test.ts`. No decision-point surface is added — the only "decision" is the pre-existing dark gate (`isStoreEmissionEnabled`) inside the generic emitter.
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `ReplicatedRecordEmitter.emit` dark gate (`stateSync.relationships.enabled`) — **pass-through** — already exists; this change merely makes `relationships` a registered emit target. Default `false` ⇒ strict no-op.
15
+ - `RelationshipManager.save`/`delete` emit funnel — **pass-through** — the manager already fires emitPut@904 / emitDelete@695,734; this change attaches a real emitter where there was `undefined` (no-op).
16
+ - `ws2SendWiring` ratchet classification — **modify** — `relationships` reclassified PENDING→WIRED (it is now genuinely send-wired).
17
+
18
+ ---
19
+
20
+ ## 1. Over-block
21
+
22
+ No block/allow surface — over-block not applicable. The emitter never rejects a manager write; a null recordKey / null projection / over-cap record is a counted no-op inside `emit()` and the local write always succeeds.
23
+
24
+ ## 2. Under-block
25
+
26
+ Not applicable (no block surface). The one "miss" worth naming: a relationship with NO channels has no cross-machine identity surface (`deriveRelationshipRecordKey` → null) and is intentionally not replicated — by design (REQ-D17), not a gap; such a record is local-only and surfaces nowhere on the peer.
27
+
28
+ ## 3. Level-of-abstraction fit
29
+
30
+ Correct layer. The generic `ReplicatedRecordEmitter` + `ReplicatedStoreReader` substrate (built in #1168) is the right home; this change is a table-row registration onto it, exactly as the spec's `tracked-next-work` (WS2-SEND-2) prescribes. No new parallel mechanism; the disclosure-minimized projection lives in `RelationshipsReplicatedStore` where the receive-side schema already lives.
31
+
32
+ ## 4. Signal vs authority compliance
33
+
34
+ Compliant. This adds NO blocking authority. The emitter is an additive, best-effort signal-producer: a failure (builder throw, journal fault) is swallowed + counted, never propagated, so the manager's local write can never fail because replication did (`@silent-fallback-ok` documented at both call sites). The union read is advisory (HIGH tier = append-both-and-flag; it injects both variants as hints, never blocks). Per `docs/signal-vs-authority.md`, this is a detector/replicator with no gate authority.
35
+
36
+ ## 5. Interactions
37
+
38
+ - Shares `server.ts`'s single `replicatedRecordEmitter` + the existing `relationshipsUnionReader` (already constructed @3927, previously unconsumed for send). No double-fire: emitPut rides the single `save()` persistence funnel; emitDelete rides the single `delete()` path.
39
+ - Does not shadow or race learnings — distinct store key (`relationships` vs `learnings`), distinct journal kind (`relationship-record`).
40
+ - Touches the same `server.ts` + `ws2SendWiring.ts` as the other five WS2-SEND stores → those PRs must SERIALIZE (stated in the run plan); no parallel WS2-SEND PRs.
41
+
42
+ ## 6. External surfaces
43
+
44
+ Dark by default (`multiMachine.stateSync.relationships.enabled` defaults false). With the flag off: byte-identical single-machine behavior — nothing crosses the wire, no external surface changes. With the flag on (multi-machine, opt-in): the disclosure-minimized, channel-keyed projection of a relationship crosses to paired machines — NEVER the local UUID `id`, NEVER the raw on-disk blob (REQ-M4). A received record is rendered as quoted `<replicated-untrusted-data>`, never authoritative for inbound-principal identity resolution (which stays local-only, REQ-M14). At-rest honesty already documented in the WS2.3 spec + CLAUDE.md relationships section.
45
+
46
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
47
+
48
+ **Replicated** — this IS a cross-machine replication path. Replication path: `RelationshipManager.save/delete` → `ReplicatedRecordEmitter.emit` → `CoherenceJournal.emitReplicatedRecord` (own-stream) → peer serve/apply → `ReplicatedPeerStreamReader.loadOriginRecords` → `relationshipsUnionReader` (no-clobber union, conflict-flagged). Identity across machines = the CHANNEL SET (not the per-machine UUID), so the same person on two machines collapses to one recordKey. Deletes propagate as channel-keyed tombstones so an erased person stays erased even on an offline-then-rejoining peer (REQ-D4). No user-facing notice surface (no one-voice gating needed). No topic-transfer state to strand. No generated URLs.
49
+
50
+ ## 8. Rollback cost
51
+
52
+ Trivial. The feature is dark by default; if wrong in production it affects only agents that explicitly enabled `stateSync.relationships`. Back-out = revert the three-file commit (or set the flag false — instant, no restart-coupled data migration). The receive-side + envelope schema already shipped and are unaffected. No data migration, no agent-state repair.