@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 +107 -0
- package/docs/1.0.0-ACCEPTANCE.md +104 -25
- package/package.json +5 -4
- package/src/plugin/tools.mjs +114 -2
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.**
|
package/docs/1.0.0-ACCEPTANCE.md
CHANGED
|
@@ -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
|
|
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` | **
|
|
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.
|
|
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
|
-
|
|
176
|
+
### ENFORCED — the pre-flight goes red without it
|
|
156
177
|
|
|
157
|
-
|
|
|
158
|
-
|
|
159
|
-
|
|
|
160
|
-
|
|
|
161
|
-
|
|
|
162
|
-
|
|
|
163
|
-
| **
|
|
164
|
-
|
|
|
165
|
-
|
|
|
166
|
-
|
|
|
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.
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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.
|
|
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",
|
package/src/plugin/tools.mjs
CHANGED
|
@@ -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.
|
|
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
|
{
|