@twinsearth/w2m-dsh-plugin 1.0.11 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,61 @@
2
2
 
3
3
  All notable changes to this project are documented here.
4
4
 
5
+ ## [1.0.12] — 2026-10-17
6
+
7
+ **The five fleet-memory actions are verified end to end, against a real relay, a real profile and a real
8
+ `fleet-memory.md` -- and the first attempt found a real defect.**
9
+
10
+ ### What was unverified until now
11
+
12
+ `publish`, `pull`, `latest`, `diff` and `merge` had each been shipped marked **UNVERIFIED**. The merge *rule* was
13
+ proven exhaustively as a pure function, and the relay *routes* were proven against a real relay -- **but nothing had
14
+ ever executed the tool surface itself.** A caller with no execution is indistinguishable from a working one, which is
15
+ the same gap this project keeps restating in a different shape.
16
+
17
+ ### The defect the end-to-end test found on its first run
18
+
19
+ `latest` reported `published_by: null` for a publication whose author the relay knew perfectly well.
20
+
21
+ `RabbitState.putFleetBlob` accepted a `publishedBy` argument that **nothing ever supplied**: the pointer store took
22
+ it, defaulted it to `null`, and the field looked implemented. Fixed by filling it from **the authenticated device's
23
+ `machine_id`, never from the request body** -- for the same reason the P2P announce route reads `machine_id` off the
24
+ token: a caller that could name itself in the body could attribute a publication to a machine that never made it.
25
+
26
+ **No unit test could have caught this**, because the unit under test was the pointer store, and it was behaving
27
+ exactly as written. The bug lived in the seam between two correct components, which is what an end-to-end test is for.
28
+
29
+ ### What the test exercises, in the order a machine would
30
+
31
+ A real relay on an ephemeral port and a real profile, then:
32
+
33
+ 1. **`publish`** — returns a 64-hex hash and the byte count.
34
+ 2. **`latest`** — discovers that hash **without being told it**, and reports the publishing machine.
35
+ 3. **`pull latest`** — fetches the bytes, re-hashes them, and they round-trip **exactly**.
36
+ 4. **`diff`** — on an unchanged document: `same: true`, no difference, **and the local file untouched**.
37
+ 5. the fleet's document is replaced out of band through the relay's own route;
38
+ 6. **`diff`** — now reports the divergence, and the local file is **still untouched**;
39
+ 7. **`merge`** — applies the declared rule (neither side declares a repair, so the **default layer** applies and the
40
+ newer publication wins), records the decision with a reason, carries the winning value into the merged text, and
41
+ **writes nothing to the local document.**
42
+
43
+ **`wrote_anything: false` is asserted three times**, because "these actions never edit the file they read" is the
44
+ property that makes it safe to let a network round trip participate at all.
45
+
46
+ ### Verified
47
+
48
+ - the end-to-end suite: 1 test, passing, covering all five actions;
49
+ - `test/fleet-merge.test.mjs`: 13 tests over the rule itself;
50
+ - full suite: **2374 tests in 91 files, 2360 pass, 0 fail, 14 skip**;
51
+ - pre-flight **GREEN, 15 of 15 checks**.
52
+
53
+ ### NOT verified, even now
54
+
55
+ **Concurrency.** Everything here is one machine at a time. Two machines publishing and merging simultaneously is
56
+ untested, and the pointer has no compare-and-swap, so the last writer to the pointer wins -- which is **the same rule
57
+ as the default layer**, but that is a coincidence rather than a guarantee. Recorded so the next person does not read
58
+ "end to end" as "under contention".
59
+
5
60
  ## [1.0.11] — 2026-10-17
6
61
 
7
62
  **`w2m_memory {"action":"merge"}`: merge this machine's collective memory with the fleet's current blob, under the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twinsearth/w2m-dsh-plugin",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Run one project across your own Windows and macOS machines from a single DeepSeek account, through 11 w2m_* tools. Four coordination modes (replicate, split, broadcast, pipeline) and six aggregate verdicts (consistent, divergent, divergent-platform, failed, partial, unverifiable). P2P hole punching by default, with a bundled relay, STUN responder and ledger fallback, so the fleet needs no third-party service. Joining needs a pairing code plus autoStartAgent: true; installing alone does not join.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -117,13 +117,13 @@
117
117
  }
118
118
  },
119
119
  "scripts": {
120
- "test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-crypto.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs",
120
+ "test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/fleet-memory-e2e.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-crypto.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs",
121
121
  "test:e2e": "node --test --test-force-exit test/e2e.test.mjs test/crossnetwork.test.mjs test/signing-e2e.test.mjs test/outbound-only.test.mjs test/recovery.test.mjs test/pipeline-e2e.test.mjs test/p2p-agent.test.mjs",
122
122
  "test:relay": "node --test --test-force-exit test/relay.test.mjs",
123
123
  "test:agent": "node --test --test-force-exit test/agent.test.mjs test/memory.test.mjs",
124
124
  "test:tools": "node --test --test-force-exit test/tools.test.mjs",
125
125
  "test:crossnetwork": "node --test --test-force-exit test/crossnetwork.test.mjs",
126
- "test:all": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/e2e.test.mjs test/crossnetwork.test.mjs test/signing-e2e.test.mjs test/outbound-only.test.mjs test/recovery.test.mjs test/pipeline-e2e.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-agent.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/p2p-crypto.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs",
126
+ "test:all": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/fleet-memory-e2e.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/e2e.test.mjs test/crossnetwork.test.mjs test/signing-e2e.test.mjs test/outbound-only.test.mjs test/recovery.test.mjs test/pipeline-e2e.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-agent.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/p2p-crypto.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs",
127
127
  "pack": "node scripts/pack.mjs",
128
128
  "verify:join": "node scripts/verify-join.mjs",
129
129
  "lint": "eslint .",
@@ -138,6 +138,7 @@
138
138
  "test:approval-index": "node --test --test-force-exit test/approval-index.test.mjs",
139
139
  "test:fleet-sync": "node --test --test-force-exit test/fleet-sync.test.mjs",
140
140
  "test:fleet-merge": "node --test --test-force-exit test/fleet-merge.test.mjs",
141
+ "test:fleet-memory-e2e": "node --test --test-force-exit test/fleet-memory-e2e.test.mjs",
141
142
  "test:health": "node --test --test-force-exit test/health.test.mjs",
142
143
  "test:storage": "node --test --test-force-exit test/storage.test.mjs",
143
144
  "test:exit-channel": "node --test --test-force-exit test/exit-channel.test.mjs",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "twinsearth-w2m-dsh",
4
- "version": "1.0.11",
4
+ "version": "1.0.12",
5
5
  "description": "Run one project across your own Windows and macOS machines from a single DeepSeek account, through 11 w2m_* tools. Four coordination modes (replicate, split, broadcast, pipeline) and six aggregate verdicts (consistent, divergent, divergent-platform, failed, partial, unverifiable). P2P hole punching by default, with a bundled relay, STUN responder and ledger fallback, so the fleet needs no third-party service. Joining needs a pairing code plus autoStartAgent: true; installing alone does not join.",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -211,7 +211,7 @@ const MAX_STAGES = 16;
211
211
  * `package.json` during `scripts/pack.mjs` rather than hand-maintained, and `W2M_PLUGIN_VERSION`
212
212
  * overrides it at runtime for tests and packagers.
213
213
  */
214
- const PLUGIN_VERSION = '1.0.11';
214
+ const PLUGIN_VERSION = '1.0.12';
215
215
 
216
216
  /** How long any single HTTP call may take before it is aborted, in milliseconds. */
217
217
  const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
@@ -560,7 +560,20 @@ export class RabbitState {
560
560
  }
561
561
  const scope = this.scopeBlobFleet(device);
562
562
  if (scope.ok !== true) return { ok: false, reason: scope.reason, detail: scope.text };
563
- return this._persist.putBlob(bytes, { fleetId: scope.fleetId });
563
+ /**
564
+ * v1.0.12: `publishedBy` comes from **the authenticated device, never from the request body.**
565
+ *
566
+ * The relay knows which device is writing; the request does not get a say, for the same reason `machine_id` is
567
+ * read off the token in the P2P announce route -- a caller that could name itself in the body could attribute a
568
+ * publication to a machine that never made it.
569
+ *
570
+ * An end-to-end test caught this field being `null`: the pointer store accepted a `publishedBy` argument that
571
+ * nothing supplied, so `latest` reported `published_by: null` for a publication whose author was right there.
572
+ */
573
+ return this._persist.putBlob(bytes, {
574
+ fleetId: scope.fleetId,
575
+ publishedBy: typeof device?.machine_id === 'string' ? device.machine_id : null,
576
+ });
564
577
  }
565
578
 
566
579
  /**