@twinsearth/w2m-dsh-plugin 0.39.0 → 0.41.0

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
@@ -4,6 +4,113 @@ All notable changes to this project are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
5
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.41.0] — 2026-10-15
8
+
9
+ **`field-authority.mjs` has a caller. The correction from 0.29.0 lands, six versions after the claim.**
10
+
11
+ ### What was wired, and the claim it settles
12
+
13
+ 0.29.0's entry said *"the connection now exists."* **It existed inside that module and nowhere else** — the
14
+ 0.36.0 classification reported the file as an **orphan**, and reading the tree rather than the changelog
15
+ confirmed it: `grep 'field-authority' src/` matched **comments and documentation only.**
16
+
17
+ `w2m_memory` now accepts a received CAT7 block **field by field, at the actor's authority level**, and writes it
18
+ as a row in the same JSONL the conclusions use — one store, one rotation, one answer to *"what does this machine
19
+ remember."* Measured, all six cases:
20
+
21
+ ```
22
+ no level ok=false MEMORY_BLOCK_NO_AUTHORITY
23
+ level 0 ok=true accepted=[subject,body] refused=[evidence]
24
+ level 70 ok=true accepted=[subject,body,evidence]
25
+ tampered ok=false BLOCK_HASH_MISMATCH
26
+ no ancestor ok=false BLOCK_MISSING_PARENT
27
+ with ancestor ok=true accepted 3/3
28
+ ```
29
+
30
+ **A refused field is OMITTED, not set to null** — v4.8.0's first falsifying test, now reachable through a tool.
31
+ **An unset level REFUSES rather than accepting the block whole**, because accepting whole is the exact behaviour
32
+ field-level acceptance was built to replace, and it would look like success. **A block naming an ancestor the
33
+ caller cannot produce is refused, never re-rooted**, because re-rooting turns an unverifiable history into no
34
+ history — which reads as a fresh, independent assertion. And **refusals are stored**, not discarded: what a
35
+ machine declined to believe is as much a part of its memory as what it took.
36
+
37
+ ### The list policed itself, which is the part worth keeping
38
+
39
+ `field-authority` was removed from `scripts/accepted-orphans.mjs` **not by a decision to tidy up, but because
40
+ the gate went red.** Its stale-entry check fails when an accepted orphan stops being an orphan — so the moment
41
+ `tools.mjs` imported the file, the run demanded the acceptance be withdrawn. **Orphans: 8 → 7. Exports: 72 →
42
+ 65.**
43
+
44
+ ### My own error, and it is the eighth of this exact shape
45
+
46
+ I inserted the new branch by matching an **anchor string** and never checked **what block that string was
47
+ inside.** It landed inside `if (conclusionText !== null)`, so the feature only ran when the caller *also* passed
48
+ `record`. Six tests failed, and I diagnosed it by **guessing at return shapes twice before printing them** —
49
+ `verifyBlock` and `acceptWithAuthority` were both exactly what I had assumed, and the actual fault was a
50
+ scoping mistake three steps earlier.
51
+
52
+ **Two wrong hypotheses, then one printed value, then ten seconds to the answer.** That is the eighth time in
53
+ this span that printing would have been faster than predicting, and the note is here because the pattern has
54
+ now survived being written down seven times.
55
+
56
+ **6 new tests, 0 failures.**
57
+
58
+ ## [0.40.0] — 2026-10-15
59
+
60
+ **The acceptance document, converged — and bound to the pre-flight so it cannot drift.**
61
+
62
+ ### Three statuses, decided by a checkable rule
63
+
64
+ Every criterion now carries one of three values, and **the rule decides it rather than opinion**:
65
+
66
+ | Status | Test |
67
+ |---|---|
68
+ | **ENFORCED** | delete the thing and run `scripts/preflight.mjs`; it goes red |
69
+ | **MEASURED** | something checks and prints, and the run stays green |
70
+ | **UNVERIFIED** | nothing checks it, or the check's own documentation says it cannot see this |
71
+
72
+ **MEASURED is not ENFORCED, and §4 says so with the receipt:** the reachability classifier named **465
73
+ exports** and could not tell a defect from a library, so a gate built on it would have been disabled within a
74
+ week. **A check that cannot fail is a report.**
75
+
76
+ **17 criteria are ENFORCED**, including §1.5 — the orphan gate added in 0.39.0. **Six are MEASURED**, of which
77
+ two are the ones worth naming: **§1.6, because the classifier cannot classify**, and **§3.10, because
78
+ `field-authority` still has no production caller.** **Seven are UNVERIFIED**, and `4.7` is the widest of them —
79
+ **the plugin this machine runs is thirty versions behind the source under test**, so every source-level
80
+ conclusion about its runtime is void.
81
+
82
+ ### The corrections, kept rather than tidied away
83
+
84
+ Seven claims were written down in this span, acted on, and later found wrong. **Four of the seven were a claim
85
+ about wiring or delivery that a single check disproved:**
86
+
87
+ * *"v4.8.0 shipped"* — the block and lineage are real; **the authority-driven half was never built.**
88
+ * *"the already-shipped rate limiter as its front door"* — **never built.**
89
+ * *"**the connection now exists**"* (CHANGELOG 0.29.0, of `field-authority`) — **it exists inside that module
90
+ and nowhere else.**
91
+ * *"v4.10.0 shipped in 0.11.0"* — the falsifying tests **are** asserted, and **nothing routed a question to
92
+ it** until 0.37.0.
93
+
94
+ **Two of the seven were corrected by the same instrument** — the reachability classifier, which was built
95
+ *because* the first two had happened, and which then found two more. **That is the only argument for a check
96
+ this project accepts: it found something the previous rounds had missed.**
97
+
98
+ ### The document is now checked, because a parallel list is a second source of truth
99
+
100
+ `test/acceptance-doc.test.mjs` reads the pre-flight's own `DECLARED_CHECKS` and fails if the document names a
101
+ gate that does not exist, if any of the three statuses stops being defined, or if the corrections section is
102
+ emptied. **A criterion cannot quietly become unenforced while the file keeps claiming otherwise.**
103
+
104
+ ### Three errors of mine, all in the test rather than the subject
105
+
106
+ * I demanded eight **exact** check names; the document sensibly uses short forms.
107
+ * I matched `gate` inside `approval-gate.mjs` and flagged **a filename as a gate**.
108
+ * I then demanded the document name `lint` and `mojibake` — **gates on the code's tidiness, not criteria for
109
+ what 1.0.0 must prove.** The document was right not to list them. **A test asserting its own assumption is
110
+ indistinguishable from a test asserting a property, until it fails.**
111
+
112
+ **6 new tests, 0 failures.**
113
+
7
114
  ## [0.39.0] — 2026-10-15
8
115
 
9
116
  **The orphan gate: a module nothing reaches now fails the pre-flight unless a reason is written down.**
@@ -3,11 +3,20 @@
3
3
  **Status: DRAFT, and deliberately incomplete.** Nothing here is an announcement that 1.0.0 is close. This file
4
4
  exists because the goal names three properties — **分模块 / 分层级 / 分权限** — and a version number means
5
5
  nothing without a definition that can fail. It is written to be argued with, and every row says **where the
6
- claim is checked and whether that check is actually running**.
6
+ claim is checked and whether that check can actually fail**.
7
7
 
8
8
  The rule applied throughout: **a criterion that nothing measures is not a criterion.** Where a check exists
9
9
  but has no caller, that is stated in its own column rather than hidden behind a tick.
10
10
 
11
+ **Every criterion carries one of three statuses, decided by a rule rather than by opinion** — **ENFORCED**
12
+ (the pre-flight goes red without it), **MEASURED** (something checks and prints, and the run stays green), or
13
+ **UNVERIFIED**. **MEASURED is not ENFORCED**, and §4 says how to tell them apart in one command.
14
+
15
+ **The statuses in §4 are themselves checked.** `test/acceptance-doc.test.mjs` reads the pre-flight's own
16
+ `DECLARED_CHECKS` list and fails if this document names a check that does not exist — so a criterion cannot
17
+ silently become unenforced while this file keeps claiming otherwise. **A document that restates what a check
18
+ does is a second source of truth unless something binds the two together.**
19
+
11
20
  ---
12
21
 
13
22
  ## 1. 分模块 — modules with a declared identity
@@ -117,7 +126,7 @@ limiter down to `util`, **not** to add the edge.
117
126
  | 3.7 | Nothing destructive auto-settles, at any level | `mayAutoSettle`, asserted | **yes** |
118
127
  | 3.8 | An escalated dispatch reaches a queue an approver can answer | `w2m_approvals`, asserted end to end | **yes** |
119
128
  | 3.9 | The queue's front door meters **raising only** | `approval-gate.mjs`, wired in 0.31.0 | **yes** |
120
- | 3.10 | Field-level memory acceptance is driven by the same model | `field-authority.mjs` | **mechanism yes, caller NO — see §4** |
129
+ | 3.10 | Field-level memory acceptance is driven by the same model | `field-authority.mjs`, called by `w2m_memory` since 0.41.0 | **yes** — the caller arrived six versions after the mechanism |
121
130
  | 3.11 | **The gate is ON by default** | `requireAuthorityLevel`, asserted | **NARROWED — see below** |
122
131
  | 3.12 | A level is chosen by name, never typed | `authority-choice.mjs`, asserted | **yes** |
123
132
  | 3.13 | A misconfigured level is reported, not silently ignored | `w2m_status.actor_authority` | **yes** |
@@ -150,33 +159,103 @@ decision about who this release is for, not a defect to fix.
150
159
 
151
160
  ---
152
161
 
153
- ## 4. What is explicitly NOT claimed
162
+ ## 4. The status of every criterion, in three values
163
+
164
+ **One rule decides the status, and it is checkable rather than a matter of opinion:**
165
+
166
+ | Status | What it means | How to tell |
167
+ |---|---|---|
168
+ | **ENFORCED** | the pre-flight **fails** if this stops being true | delete the thing and run `scripts/preflight.mjs`; it goes red |
169
+ | **MEASURED** | something checks it and **prints**, but the run stays green | the check exists and exits 0 either way |
170
+ | **UNVERIFIED** | nothing checks it, or the check cannot see this | there is no check, or its own documentation says it cannot |
171
+
172
+ **MEASURED is not ENFORCED**, and this project has already paid for confusing them: the reachability classifier
173
+ named **465 exports** and could not say which were defects, so a gate built on it would have been turned off
174
+ within a week. **A check that cannot fail is a report.**
154
175
 
155
- Recorded here so that absence is a decision rather than an omission:
176
+ ### ENFORCED — the pre-flight goes red without it
156
177
 
157
- | Not claimed | Why |
158
- |---|---|
159
- | **Reachability of every module** | nothing measures it (§1.5). Three uncalled mechanisms were shipped in this span. |
160
- | **The gate is active** | `actorAuthorityLevel` is unset by default (§3.11). |
161
- | **Field authority reaches the memory layer** | `field-authority.mjs` has no production caller; `w2m_memory` does not use it. |
162
- | **The queue survives a restart** | it is per-plugin-lifetime by design, and is **not** an audit log. |
163
- | **Any scale claim** | this fleet is 3 usable machines of 5 paired. `ROADMAP.md` refuses to plan v4.11.0+ for exactly this reason, and `fleet-scale.mjs` refuses to extrapolate from five. |
164
- | **MacMini verification of everything** | four suites were verified on that machine in 0.28.0; the other 69 were not. **Measured in 0.32.0: the eight suites tried there did not pass, and the cause is not yet established.** |
165
- | **The eight suites tried on MacMini in 0.32.0** | every one of them returned non-zero there while passing here. The likely cause is a missing `node_modules` on that machine, but **that was not measured**, so it is a hypothesis and not a finding. |
166
- | **`P2P_PUNCH_FAILED`** | cause UNVERIFIED; three hypotheses measured and excluded. |
178
+ | # | Criterion | The gate |
179
+ |---|---|---|
180
+ | 1.1 | every module belongs to exactly one declared layer | `layer-check` |
181
+ | 1.2 | every dependency edge is declared, and every declared edge is used | `layer-check` |
182
+ | 1.3 | every suite on disk is named by a script | `test wiring` |
183
+ | 1.4 | every document's claim about the artifact matches the artifact | `claims`, 48 claims |
184
+ | **1.5** | **no orphan module without a written reason** | **`reachable or accepted`** — **7 orphans, 65 exports, down from 8 and 72** |
185
+ | 2.1–2.4 | ranked layers, an allow-list of edges, no upward edge | `layer-check` |
186
+ | 3.2 | every declared capability is reachable by some band | `auditCapabilities`, asserted |
187
+ | 3.4 | the dispatch path consults the gate | `tools.test.mjs`, end to end |
188
+ | 3.5 | an undeclared capability is refused | `permission.test.mjs` |
189
+ | 3.6 | escalation is exactly one band | asserted across the whole table |
190
+ | 3.7 | nothing destructive auto-settles | `mayAutoSettle`, asserted |
191
+ | 3.9 | the queue's front door meters raising only | `approval-gate.test.mjs` |
192
+ | 3.12 | a level is chosen by name, never typed | `authority-choice.test.mjs` |
193
+ | 3.13 | a misconfigured level is reported | `w2m_status.actor_authority` |
194
+ | 5.1 | the packed tarball registers all eleven tools | `packed tarball` |
195
+ | 5.2 | the CHANGELOG is newest-first with a title above the newest entry | `CHANGELOG shape` |
196
+
197
+ ### MEASURED — checked, printed, and able to fail silently
198
+
199
+ | # | Criterion | What is actually measured |
200
+ |---|---|---|
201
+ | 1.6 | **reachability itself** | 65 orphan exports, 14 cli. **The classifier cannot tell a defect from a library**, so the *decision* is enforced (§1.5) while the *classification* is not |
202
+ | 3.1 | capabilities declared with a cost | the table is read by `permission.mjs`; a new capability with no caller would still be listed |
203
+ | ~~3.10~~ | ~~field-level memory acceptance driven by the authority model~~ | **PROMOTED TO ENFORCED in 0.41.0.** It was the one `interface-unrouted` orphan whose removal had a known answer, and `w2m_memory` now accepts a received block field by field at the actor's level |
204
+ | 3.11 | the gate is on by default | `requireAuthorityLevel` makes it enforceable **per machine**; the default is still off |
205
+ | 4.1 | four suites run on MacMini | verified once, in 0.28.0, with that machine's exit code. **Nothing re-runs them** |
206
+
207
+ ### UNVERIFIED — nothing checks this, and saying so is the point
208
+
209
+ | # | Criterion | Why it is unverified |
210
+ |---|---|---|
211
+ | 4.2 | the other 69 suites on MacMini | eight were tried in 0.32.0 and failed; **the cause is a hypothesis, not a finding** |
212
+ | 4.3 | `w2m_council` as a *tool* | its **modules** ran on MacMini (7 phases, dissent, recount, exit 0); the **tool registration path** has only ever run here |
213
+ | 4.4 | `P2P_PUNCH_FAILED` | cause unverified; **three hypotheses measured and excluded** (BLAKE2s, the wider primitive set, version skew) |
214
+ | 4.5 | the queue survives a restart | it does not, by design — and it is **not** an audit log |
215
+ | 4.6 | any scale claim | 3 usable machines of 5. `fleet-scale.mjs` **refuses to extrapolate** from five, and `ROADMAP.md` refuses to plan v4.11.0+ for the same reason |
216
+ | 4.7 | the plugin this machine runs is current | **installed 0.9.5 against 0.39.0 of source — thirty versions apart.** Every source-level conclusion about this machine's runtime is void |
167
217
 
168
218
  ---
169
219
 
170
- ## 5. What would make this file finished
220
+ ## 5. Claims this work corrected, kept rather than tidied away
221
+
222
+ A document that only shows its final state hides how it got there, and **every entry below was a statement that
223
+ was written down, acted on, and later found wrong.** They are kept because the pattern is the finding: four of
224
+ the seven were **a claim about wiring or delivery that a single check disproved.**
171
225
 
172
- 1. **A reachability check** — every exported function has a non-test caller, or is declared as
173
- interface-only with a reason. Without it, §1.5 stays open and the class of defect that produced three
174
- dead mechanisms stays invisible.
175
- 2. **A decision on §3.11**, whichever way it goes, reflected in `README.md` and in the pre-flight.
176
- 3. **Every row above promoted to a check that runs**, so this document becomes a description of the
177
- pre-flight rather than a parallel list that can drift from it. **A document that restates what the
178
- pre-flight does is a second source of truth, and this project has already paid for one of those.**
226
+ | Claim | Where it was made | What checking found |
227
+ |---|---|---|
228
+ | "v4.8.0 shipped, suite verified on MacMini" | `ROADMAP.md` | the block and lineage are real; **"per-field acceptance driven by the authority model" was never built** — `blocks.mjs` did not import `authority.mjs` at all |
229
+ | "the already-shipped rate limiter as its front door" | `ROADMAP.md`, v4.9.0 | **never built** — `approval-index.mjs` imports `authority.mjs` and nothing else |
230
+ | "**the connection now exists**" | CHANGELOG 0.29.0, of `field-authority` | **it exists inside that module and nowhere else.** Nothing imports it; it is an accepted orphan today |
231
+ | "v4.10.0 shipped in 0.11.0" | `ROADMAP.md` | the three falsifying tests **are** asserted — and **nothing routed a question to it.** Fixed in 0.37.0 by `w2m_council` |
232
+ | "zero is the all-clear" | `machine-test-packet.mjs` | false for every count but one; **zero means every file was ABSENT** — a total failure read as a clean run |
233
+ | "the eight suites failed on MacMini because of `node_modules`" | CHANGELOG 0.32.0 | recorded as a hypothesis then, **and measured in 0.38.0**: `council.mjs` runs there because it needs nothing, so the suites fail on **what their fixtures reach for** |
234
+ | "`log-bounds` fixes the 40× log growth" | CHANGELOG 0.12.0 | the module is correct and tested; **nothing calls it.** It is on the accepted-orphan list as `interface-unrouted` |
235
+
236
+ **Two of these seven were corrected by the same instrument** — the reachability classifier, which was built
237
+ *because* the first two had happened. **That is the only argument for a check that this project accepts: it
238
+ found something the previous three rounds had missed.**
239
+
240
+ ---
179
241
 
180
- This draft deliberately stops short of those three, because each is a decision about what 1.0.0 must be
181
- rather than a defect to fix, and the person who owns it has not answered the question this session asked in
182
- round 12: **write the acceptance definition, or keep advancing the roadmap?**
242
+ ## 6. What would make this file finished
243
+
244
+ 1. **Flip §3.11's default.** `requireAuthorityLevel` exists and is tested; whether 1.0.0 ships with it on is a
245
+ decision about who the release is for. **A permission system that is off is one that has never run.**
246
+ 2. **Wire the remaining three `interface-unrouted` orphans**, or delete them. `health` is the sensor the user
247
+ asked for by name; `log-bounds` is the answer to a measured 40× growth; `storage` holds the undelatable
248
+ tiers. **Each is on the list with what would remove it.**
249
+ ~~`field-authority`~~ **was the fourth and is DONE — 0.41.0 gave it the caller its entry named.** It is
250
+ worth noting what removed it: **not a decision to tidy up, but the gate's stale-entry check going red** the
251
+ moment something imported the file. The list policed itself.
252
+ 3. **Re-run the 69 unverified suites on MacMini**, which needs `node_modules` there — an environment change
253
+ nobody has made.
254
+ 4. **Promote every MEASURED row to ENFORCED**, so this document stops being a parallel list that can drift from
255
+ the pre-flight. **A document that restates what a check does is a second source of truth, and this project
256
+ has already paid for one of those.**
257
+
258
+ **This file is honest about being unfinished, which is the only useful thing a 1.0.0 definition can be before
259
+ 1.0.0 exists.** The question it was written in answer to — *write the acceptance definition, or keep advancing
260
+ the roadmap?* — was asked in round 12 and answered by writing it. **Whether to act on items 1–4 is a decision
261
+ the person who owns this repository still has to make.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twinsearth/w2m-dsh-plugin",
3
- "version": "0.39.0",
3
+ "version": "0.41.0",
4
4
  "description": "DSH: Windows2MacOS — drive the same project on online Windows and macOS machines from one DeepSeek account, across networks and regions. P2P hole punching is the default path (relay + shared server as rendezvous, ledger and fallback), with a bundled STUN responder, Tailscale/public-VPS/tunnel shapes, Rabbit relay + per-machine Localside + eight DSH tools (replicate / split), nine aggregate verdicts and signed requests.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -113,13 +113,13 @@
113
113
  }
114
114
  },
115
115
  "scripts": {
116
- "test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.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/layer-model.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/chatops-fleet-join.test.mjs",
116
+ "test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.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/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/chatops-fleet-join.test.mjs",
117
117
  "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",
118
118
  "test:relay": "node --test --test-force-exit test/relay.test.mjs",
119
119
  "test:agent": "node --test --test-force-exit test/agent.test.mjs test/memory.test.mjs",
120
120
  "test:tools": "node --test --test-force-exit test/tools.test.mjs",
121
121
  "test:crossnetwork": "node --test --test-force-exit test/crossnetwork.test.mjs",
122
- "test:all": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.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/layer-model.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/chatops-fleet-join.test.mjs",
122
+ "test:all": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.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/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/chatops-fleet-join.test.mjs",
123
123
  "pack": "node scripts/pack.mjs",
124
124
  "verify:join": "node scripts/verify-join.mjs",
125
125
  "lint": "eslint .",
@@ -154,7 +154,8 @@
154
154
  "reachability": "node scripts/reachability-check.mjs",
155
155
  "reachability:classify": "node scripts/reachability-classify.mjs",
156
156
  "test:council-session": "node --test --test-force-exit test/council-session.test.mjs",
157
- "orphans": "node --test --test-force-exit test/accepted-orphans.test.mjs"
157
+ "orphans": "node --test --test-force-exit test/accepted-orphans.test.mjs",
158
+ "test:acceptance-doc": "node --test --test-force-exit test/acceptance-doc.test.mjs"
158
159
  },
159
160
  "keywords": [
160
161
  "dsh-bundle",
@@ -98,6 +98,12 @@ import path from 'node:path';
98
98
  // skipped rather than fatal, and the rotated generation reachable -- and a second implementation would
99
99
  // drift from the writer's shape without anything failing.
100
100
  import { appendMemory, readMemorySync } from '../agent/memory.mjs';
101
+ // v0.41.0: v4.8.0's other half. Written in 0.9.9 and 0.29.0, and **nothing called them** until here --
102
+ // `field-authority.mjs` was reported as an orphan in 0.36.0 and stayed on the accepted list until 0.41.0.
103
+ // `makeBlock` is NOT imported: this path receives blocks and only reads them. Building one here would be the
104
+ // plugin asserting a claim on its own behalf, which is the sender's job.
105
+ import { acceptBlock, verifyBlock } from '../agent/blocks.mjs';
106
+ import { acceptWithAuthority } from '../agent/field-authority.mjs';
101
107
 
102
108
  // The base-address rules live in one place (§2), shared with the agent side. Importing the - rather
103
109
  // than re-deriving them her - is what stops the plugin and the agent from disagreeing about what
@@ -202,7 +208,7 @@ const MAX_STAGES = 16;
202
208
  * `package.json` during `scripts/pack.mjs` rather than hand-maintained, and `W2M_PLUGIN_VERSION`
203
209
  * overrides it at runtime for tests and packagers.
204
210
  */
205
- const PLUGIN_VERSION = '0.39.0';
211
+ const PLUGIN_VERSION = '0.41.0';
206
212
 
207
213
  /** How long any single HTTP call may take before it is aborted, in milliseconds. */
208
214
  const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
@@ -5044,6 +5050,29 @@ export async function apply(ctx, config = {}) {
5044
5050
  'field a machine is not allowed to write itself -- the machine stores the fact, a reader ' +
5045
5051
  'asserts the meaning. Written as a new record so nothing already written is rewritten.',
5046
5052
  },
5053
+ accept_block: {
5054
+ type: 'object',
5055
+ description:
5056
+ 'v0.41.0. Accept a CAT7 cognitive block received from another machine, per field, DRIVEN BY AN ' +
5057
+ 'AUTHORITY LEVEL (actor_authority below) rather than accepted whole. A field the level cannot ' +
5058
+ 'afford is OMITTED from the stored block rather than set to null, and lineage is checked against ' +
5059
+ 'the ancestors you supply: a block whose parent cannot be produced is REFUSED, never silently ' +
5060
+ 're-rooted, because re-rooting turns an unverifiable history into no history. Every field decision ' +
5061
+ 'is reported, so a refusal is readable rather than mysterious.',
5062
+ },
5063
+ actor_authority: {
5064
+ type: 'number',
5065
+ description:
5066
+ 'The authority level, 0..100, for accept_block. Required with it: an unset level means there is ' +
5067
+ 'no actor to judge, and accepting a block whole would defeat the point of field-level acceptance.',
5068
+ },
5069
+ lineage: {
5070
+ type: 'array',
5071
+ items: { type: 'string' },
5072
+ description:
5073
+ 'The hashes of the ancestors you can actually produce, for accept_block. A block naming an ' +
5074
+ 'ancestor outside this list is refused rather than re-rooted.',
5075
+ },
5047
5076
  },
5048
5077
  output: jsonOutput,
5049
5078
  async execute(args, exec) {
@@ -5119,8 +5148,91 @@ export async function apply(ctx, config = {}) {
5119
5148
  * text, which is the thing a reader most needs when a conclusion turns out to be wrong.
5120
5149
  */
5121
5150
  const conclusionText = typeof args?.record === 'string' && args.record.trim() !== '' ? args.record.trim() : null;
5151
+ /**
5152
+ * v0.41.0: accept a received cognitive block, field by field, driven by the authority model.
5153
+ *
5154
+ * ## Why the block is written as a ROW rather than into a structure of its own
5155
+ *
5156
+ * The memory file is JSONL rows with a \`kind\`. A conclusion is one kind; an accepted block is
5157
+ * another. Giving blocks their own file would mean two stores to read, two to rotate, and two
5158
+ * answers to "what does this machine remember" -- and the rotation in \`appendMemory\` already
5159
+ * handles the size problem a second store would be created to solve.
5160
+ *
5161
+ * ## Why an unset authority level REFUSES rather than accepting everything
5162
+ *
5163
+ * Field-level acceptance exists because a receiver should not adopt everything a sender asserts.
5164
+ * With no level there is no actor to judge, so accepting the block whole would be the exact
5165
+ * behaviour this mode was built to replace -- and it would look like success.
5166
+ */
5167
+ // Hoisted above both branches that write: block acceptance and the conclusion. One read of the
5168
+ // configured directory, so the two cannot disagree about where memory lives.
5169
+ const stateDirForRecord = cfg.stateDir ?? null;
5170
+
5171
+ if (args?.accept_block !== undefined && args?.accept_block !== null) {
5172
+ if (stateDirForRecord === null) {
5173
+ return JSON.stringify({ ok: false, scope: 'local', reason_code: 'MEMORY_NO_STATE_DIR', detail: 'this plugin has no stateDir configured, so a block has nowhere to be accepted into' });
5174
+ }
5175
+ if (!Number.isInteger(args?.actor_authority)) {
5176
+ return JSON.stringify({
5177
+ ok: false,
5178
+ scope: 'local',
5179
+ reason_code: 'MEMORY_BLOCK_NO_AUTHORITY',
5180
+ detail: 'accept_block needs actor_authority as an integer 0..100; without a level there is no actor to judge the fields, and accepting the block whole is what field-level acceptance exists to prevent',
5181
+ });
5182
+ }
5183
+ const received = args.accept_block;
5184
+ const verified = verifyBlock(received);
5185
+ if (!verified.ok) {
5186
+ // A block that fails its own hash is refused entirely: field-level acceptance is about trust, and
5187
+ // there is nothing to trust about content that does not match what it claims to be.
5188
+ return JSON.stringify({ ok: false, scope: 'local', reason_code: verified.code, detail: verified.detail, verified: false });
5189
+ }
5190
+ const { accepted, refused, decisions } = acceptWithAuthority(
5191
+ received,
5192
+ { level: args.actor_authority, id: self },
5193
+ { accept: acceptBlock, ...(Array.isArray(args.lineage) ? { lineage: args.lineage } : {}) },
5194
+ );
5195
+ if (accepted === null) {
5196
+ // Refused as a whole -- a missing ancestor, most likely. Reported with the whole field list, because
5197
+ // "refused" without "why" is the failure this project keeps having to correct.
5198
+ return JSON.stringify({ ok: false, scope: 'local', reason_code: refused[0]?.why?.split(':')[0] ?? 'MEMORY_BLOCK_REFUSED', detail: refused[0]?.why ?? 'the block was refused', refused, decisions });
5199
+ }
5200
+ const row = {
5201
+ at: new Date().toISOString(),
5202
+ kind: 'block',
5203
+ machine_id: selfId,
5204
+ machine_name: self,
5205
+ // The block's own hash identifies what was accepted; the lineage it declared travels with the row, so
5206
+ // a reader can tell an accepted block from a fabricated one without re-deriving anything.
5207
+ block_hash: received.hash ?? null,
5208
+ lineage: Array.isArray(received.lineage) ? [...received.lineage] : [],
5209
+ accepted_by_level: args.actor_authority,
5210
+ accepted,
5211
+ accepted_fields: decisions.filter((d) => d.accepted).map((d) => d.field),
5212
+ // Refusals are stored, not discarded: "what did this machine decline to believe" is as much a part
5213
+ // of its memory as what it took, and the design's whole third test is about dissent surviving.
5214
+ refused_fields: decisions.filter((d) => !d.accepted).map((d) => ({ field: d.field, why: d.reason })),
5215
+ };
5216
+ const written = appendMemory(stateDirForRecord, row);
5217
+ return JSON.stringify({
5218
+ ok: written.ok === true,
5219
+ scope: 'local',
5220
+ recorded: written.ok === true,
5221
+ kind: 'block',
5222
+ block_hash: row.block_hash,
5223
+ accepted_fields: row.accepted_fields,
5224
+ refused_fields: row.refused_fields,
5225
+ accepted,
5226
+ decisions,
5227
+ path: written.path ?? null,
5228
+ reason_code: written.ok === true ? null : 'MEMORY_BLOCK_WRITE_FAILED',
5229
+ detail: written.ok === true
5230
+ ? `accepted ${row.accepted_fields.length} field(s) at level ${args.actor_authority}; ${row.refused_fields.length} omitted`
5231
+ : (written.error ?? 'the block could not be written'),
5232
+ });
5233
+ }
5234
+
5122
5235
  if (conclusionText !== null) {
5123
- const stateDirForRecord = cfg.stateDir ?? null;
5124
5236
  if (stateDirForRecord === null) {
5125
5237
  return JSON.stringify(
5126
5238
  {