@twinsearth/w2m-dsh-plugin 0.40.0 → 0.42.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 +100 -0
- package/README.md +156 -3
- package/docs/1.0.0-ACCEPTANCE.md +10 -7
- package/package.json +1 -1
- package/src/plugin/tools.mjs +114 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,106 @@ 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.42.0] — 2026-10-15
|
|
8
|
+
|
|
9
|
+
**The README catches up with 62 versions — and gathering the numbers found a defect that had survived all of them.**
|
|
10
|
+
|
|
11
|
+
### The progress section, in both languages
|
|
12
|
+
|
|
13
|
+
The README's newest version reference was **v0.4.0**, thirteen times, and its install command pinned a
|
|
14
|
+
**v0.6.3** tarball. **Thirty-four versions of work had no record in the file a newcomer reads first.**
|
|
15
|
+
|
|
16
|
+
There is now a **Progress** section (with a matching 进度 section) built from the CHANGELOG rather than from
|
|
17
|
+
memory: **62 versions since 0.8.0, 11 tools, 79 suites, 2199 tests, 12 pre-flight checks, 0 failures.** It
|
|
18
|
+
carries the three properties the project set out to have, the roadmap's **delivered / partial** state per
|
|
19
|
+
version, what the reachability work found, the measured-on-real-machines table, and an explicit **"this is not
|
|
20
|
+
1.0.0"**.
|
|
21
|
+
|
|
22
|
+
**The seven accepted orphans are listed with what would remove each one**, and the **three that are defects are
|
|
23
|
+
named as defects** rather than folded in with the four libraries — because the import graph cannot tell them
|
|
24
|
+
apart, and only a written reason can.
|
|
25
|
+
|
|
26
|
+
### `layer-check` printed "clean" and exited 1
|
|
27
|
+
|
|
28
|
+
Gathering the numbers meant running the checks and reading their output and their status side by side, and two
|
|
29
|
+
of them contradicted each other:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
clean: every cross-layer edge is declared
|
|
33
|
+
exit=1
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**The clean path called `process.exit(1)`.** Every reader that trusts the exit code — a shell, CI, a script,
|
|
37
|
+
`&&` — **read a passing check as a failing one.**
|
|
38
|
+
|
|
39
|
+
**The pre-flight never noticed, and the reason is the interesting part: its own step reads the PRINTED TEXT
|
|
40
|
+
rather than the status.** A check can therefore be wrong for a hundred versions while the gate over it stays
|
|
41
|
+
green. It now exits `0`, with the failure paths still distinct (`2` for a violation, `3` for unexplained), and
|
|
42
|
+
a regression test asserts **the status rather than the message** — because a test that read the message would
|
|
43
|
+
have passed against the broken version, which is exactly what happened.
|
|
44
|
+
|
|
45
|
+
### Also corrected
|
|
46
|
+
|
|
47
|
+
* the install instructions now say **how to verify you got the version you asked for**, because this
|
|
48
|
+
repository's own profile installed from a pinned tarball and **fell 30+ versions behind**. A successful
|
|
49
|
+
install and a current install are two different things.
|
|
50
|
+
* both language halves now name **`pnpm`** as the fallback where `npm` is absent — which is the case in the
|
|
51
|
+
packaged DSH runtime this was tested against.
|
|
52
|
+
* one dead in-page anchor, from assuming GitHub's slugger keeps an em dash. It does not.
|
|
53
|
+
|
|
54
|
+
**2 new tests, 0 failures.**
|
|
55
|
+
|
|
56
|
+
## [0.41.0] — 2026-10-15
|
|
57
|
+
|
|
58
|
+
**`field-authority.mjs` has a caller. The correction from 0.29.0 lands, six versions after the claim.**
|
|
59
|
+
|
|
60
|
+
### What was wired, and the claim it settles
|
|
61
|
+
|
|
62
|
+
0.29.0's entry said *"the connection now exists."* **It existed inside that module and nowhere else** — the
|
|
63
|
+
0.36.0 classification reported the file as an **orphan**, and reading the tree rather than the changelog
|
|
64
|
+
confirmed it: `grep 'field-authority' src/` matched **comments and documentation only.**
|
|
65
|
+
|
|
66
|
+
`w2m_memory` now accepts a received CAT7 block **field by field, at the actor's authority level**, and writes it
|
|
67
|
+
as a row in the same JSONL the conclusions use — one store, one rotation, one answer to *"what does this machine
|
|
68
|
+
remember."* Measured, all six cases:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
no level ok=false MEMORY_BLOCK_NO_AUTHORITY
|
|
72
|
+
level 0 ok=true accepted=[subject,body] refused=[evidence]
|
|
73
|
+
level 70 ok=true accepted=[subject,body,evidence]
|
|
74
|
+
tampered ok=false BLOCK_HASH_MISMATCH
|
|
75
|
+
no ancestor ok=false BLOCK_MISSING_PARENT
|
|
76
|
+
with ancestor ok=true accepted 3/3
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**A refused field is OMITTED, not set to null** — v4.8.0's first falsifying test, now reachable through a tool.
|
|
80
|
+
**An unset level REFUSES rather than accepting the block whole**, because accepting whole is the exact behaviour
|
|
81
|
+
field-level acceptance was built to replace, and it would look like success. **A block naming an ancestor the
|
|
82
|
+
caller cannot produce is refused, never re-rooted**, because re-rooting turns an unverifiable history into no
|
|
83
|
+
history — which reads as a fresh, independent assertion. And **refusals are stored**, not discarded: what a
|
|
84
|
+
machine declined to believe is as much a part of its memory as what it took.
|
|
85
|
+
|
|
86
|
+
### The list policed itself, which is the part worth keeping
|
|
87
|
+
|
|
88
|
+
`field-authority` was removed from `scripts/accepted-orphans.mjs` **not by a decision to tidy up, but because
|
|
89
|
+
the gate went red.** Its stale-entry check fails when an accepted orphan stops being an orphan — so the moment
|
|
90
|
+
`tools.mjs` imported the file, the run demanded the acceptance be withdrawn. **Orphans: 8 → 7. Exports: 72 →
|
|
91
|
+
65.**
|
|
92
|
+
|
|
93
|
+
### My own error, and it is the eighth of this exact shape
|
|
94
|
+
|
|
95
|
+
I inserted the new branch by matching an **anchor string** and never checked **what block that string was
|
|
96
|
+
inside.** It landed inside `if (conclusionText !== null)`, so the feature only ran when the caller *also* passed
|
|
97
|
+
`record`. Six tests failed, and I diagnosed it by **guessing at return shapes twice before printing them** —
|
|
98
|
+
`verifyBlock` and `acceptWithAuthority` were both exactly what I had assumed, and the actual fault was a
|
|
99
|
+
scoping mistake three steps earlier.
|
|
100
|
+
|
|
101
|
+
**Two wrong hypotheses, then one printed value, then ten seconds to the answer.** That is the eighth time in
|
|
102
|
+
this span that printing would have been faster than predicting, and the note is here because the pattern has
|
|
103
|
+
now survived being written down seven times.
|
|
104
|
+
|
|
105
|
+
**6 new tests, 0 failures.**
|
|
106
|
+
|
|
7
107
|
## [0.40.0] — 2026-10-15
|
|
8
108
|
|
|
9
109
|
**The acceptance document, converged — and bound to the pre-flight so it cannot drift.**
|
package/README.md
CHANGED
|
@@ -14,6 +14,71 @@ English | [中文](#中文说明)
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## Progress — where this actually is
|
|
18
|
+
|
|
19
|
+
**Current: `0.41.0`.** 62 versions since `0.8.0`. The number that matters more than the version:
|
|
20
|
+
**11 tools, 79 suites, 2199 tests, 12 pre-flight checks, 0 failures.**
|
|
21
|
+
|
|
22
|
+
### The three properties this project set out to have
|
|
23
|
+
|
|
24
|
+
| Property | State | What it means concretely |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| **分模块** (modular) | **enforced** | every file belongs to exactly one declared layer; `layer-check` fails on an undeclared edge |
|
|
27
|
+
| **分层级** (layered) | **enforced** | six ranked layers and an **allow-list** of edges — not a rule that can be interpreted, a list that can be read |
|
|
28
|
+
| **分权限** (tiered authority) | **built, opt-in** | four bands, escalation moves **exactly one band**, nothing destructive auto-settles. **Off by default** — see the gap below |
|
|
29
|
+
|
|
30
|
+
### Roadmap
|
|
31
|
+
|
|
32
|
+
| Version | Deliverable | State |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| **v4.8.0** | field-level acceptance, MMP P1/P2, lineage | **delivered** — `w2m_memory` accepts a received CAT7 block **field by field at the actor's level**; a field that costs more than the actor has is **omitted, not nulled**; a block naming an ancestor the caller cannot produce is **refused, never re-rooted** |
|
|
35
|
+
| **v4.9.0** | approval queue | **partial** — the queue, the escalation bridge and the raise-limiter are wired and reachable by an operator through `w2m_approvals`. **The "front door" named in the roadmap was never built**: `approval-index.mjs` imports `authority.mjs` and nothing else |
|
|
36
|
+
| **v4.10.0** | committee governance | **delivered** — seven phases, weighted votes, **a tie is refused rather than broken**, and a sealed decision is **retrievable with its dissent and its reasons** after the council that produced it is gone |
|
|
37
|
+
|
|
38
|
+
### What the reachability work found, because it is the honest part
|
|
39
|
+
|
|
40
|
+
Three mechanisms were shipped in this span **fully tested and reachable by nothing** — `field-authority`,
|
|
41
|
+
`approval-gate`, and `council`. Two were **described as delivered** while nothing ran them. A single `grep`
|
|
42
|
+
disproved one of them.
|
|
43
|
+
|
|
44
|
+
There is now a **gate**: a module nothing reaches makes the pre-flight **red** unless it appears in
|
|
45
|
+
`scripts/accepted-orphans.mjs` **with a reason and a statement of what would remove it**. An acceptance that
|
|
46
|
+
outlives its cause also fails. **Seven remain**:
|
|
47
|
+
|
|
48
|
+
| Module | Kind | What would remove it |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `health.mjs` | **defect** | the agent calling it on a schedule |
|
|
51
|
+
| `log-bounds.mjs` | **defect** | the same — it is the answer to a **measured 40× log growth** |
|
|
52
|
+
| `storage.mjs` | **defect** | a tool writing through its undelatable tiers |
|
|
53
|
+
| `fleet-scale.mjs`, `fleet-state.mjs`, `exit-channel.mjs`, `test-report.mjs` | library | a caller, when one is wanted |
|
|
54
|
+
|
|
55
|
+
**The defect class is named as a defect rather than folded in with the libraries**, because the two are
|
|
56
|
+
indistinguishable from the import graph alone and only a written reason can separate them.
|
|
57
|
+
|
|
58
|
+
### The gap that matters most right now
|
|
59
|
+
|
|
60
|
+
**The plugin this machine runs is 30+ versions behind this source tree.** Every source-level conclusion about
|
|
61
|
+
the running runtime is void until it is upgraded — which is one command, and it is in
|
|
62
|
+
[Install](#3-the-dsh-plugin).
|
|
63
|
+
|
|
64
|
+
### Measured on real machines
|
|
65
|
+
|
|
66
|
+
| Fact | Evidence |
|
|
67
|
+
|---|---|
|
|
68
|
+
| a **seven-phase council** runs on macOS | `exit 0` on MacMini: phases 7, dissent retained, recount reproduced |
|
|
69
|
+
| **field-level acceptance** holds on macOS | `exit 0` on MacMini: level 0 cannot reach `evidence`, level 70 can, tampering caught, a missing ancestor refused rather than re-rooted |
|
|
70
|
+
| the fleet is **3 usable machines of 5 paired** | and `fleet-scale.mjs` **refuses to extrapolate** from five |
|
|
71
|
+
| **`P2P_PUNCH_FAILED`** | **UNVERIFIED.** Three hypotheses measured and excluded (BLAKE2s-256, the wider primitive set, version skew). **The plugin that raises it runs 0.9.5** — upgrading is the next experiment |
|
|
72
|
+
|
|
73
|
+
### Not claimed
|
|
74
|
+
|
|
75
|
+
**This is not 1.0.0.** `docs/1.0.0-ACCEPTANCE.md` sorts all 30 criteria into **ENFORCED / MEASURED /
|
|
76
|
+
UNVERIFIED** by a rule rather than an opinion, names **seven claims this work had to retract**, and states
|
|
77
|
+
plainly that four of its own closing items are undone. It is bound to the pre-flight by a test, so a criterion
|
|
78
|
+
cannot quietly become unenforced while the file keeps claiming otherwise.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
17
82
|
## The problem
|
|
18
83
|
|
|
19
84
|
You have a Windows desktop and a Mac laptop. You are working on one project. You
|
|
@@ -250,9 +315,18 @@ dsh plugin --profile <profile-name> add @twinsearth/w2m-dsh-plugin
|
|
|
250
315
|
|
|
251
316
|
```bash
|
|
252
317
|
dsh plugin --profile <profile-name> add \
|
|
253
|
-
https://github.com/TwinsEarth/dsh-windows2macos/releases/download/v0.
|
|
318
|
+
https://github.com/TwinsEarth/dsh-windows2macos/releases/download/v0.41.0/twinsearth-w2m-dsh-plugin-0.41.0.tgz
|
|
254
319
|
```
|
|
255
320
|
|
|
321
|
+
This form **pins a version by construction**, which is both its virtue and the trap: a
|
|
322
|
+
profile that installs this way stays on that version forever unless somebody edits the
|
|
323
|
+
line. **This repository's own profile did exactly that and fell 30+ versions behind** —
|
|
324
|
+
so if you use it, re-check the number against the badge at the top of this file.
|
|
325
|
+
|
|
326
|
+
**No `npm`?** Some hosted runtimes (including the packaged DSH) ship `node` and `pnpm`
|
|
327
|
+
but not `npm`. `pnpm add @twinsearth/w2m-dsh-plugin@0.41.0 --save-exact` installs the
|
|
328
|
+
identical tarball from the identical registry.
|
|
329
|
+
|
|
256
330
|
**From the repository** — the form a marketplace's one-click installer uses (it clones
|
|
257
331
|
the repo, builds nothing, and registers `cordis.patch.yml`):
|
|
258
332
|
|
|
@@ -270,6 +344,14 @@ answer, not three.
|
|
|
270
344
|
> listed in `package.json` but never composed is the failure this project hit once
|
|
271
345
|
> already — see `cordis.patch.yml`'s header for what that looked like.
|
|
272
346
|
|
|
347
|
+
**Then check the version you actually got**, because a successful install and a current
|
|
348
|
+
install are two different things:
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
w2m_status → actor_authority and approvals should be present (0.9.5 has neither)
|
|
352
|
+
tool list → w2m_council should be present (it arrived in 0.37.0; anything older lacks it)
|
|
353
|
+
```
|
|
354
|
+
|
|
273
355
|
Then give it the relay URL in that profile's `cordis.patch.yml` — or leave
|
|
274
356
|
`rabbitUrl` empty to use the shared server:
|
|
275
357
|
|
|
@@ -532,6 +614,10 @@ Read this before exposing the relay to anything.
|
|
|
532
614
|
|
|
533
615
|
This project's rule is that claims carry their evidence.
|
|
534
616
|
|
|
617
|
+
> **Current state lives in [Progress](#progress-where-this-actually-is) above.** What follows is the
|
|
618
|
+
> **v0.4.0 record**, kept because it is the measurement the direct path still rests on — not because it
|
|
619
|
+
> describes what ships today. The version is now `0.41.0`.
|
|
620
|
+
|
|
535
621
|
### v0.4.0 — the direct path is the default, and here is what that is worth
|
|
536
622
|
|
|
537
623
|
v0.3.9 shipped the P2P transport and admitted in its own §8 that nothing read
|
|
@@ -778,6 +864,60 @@ When something does not work, start from the symptom: [TROUBLESHOOTING.md](TROUB
|
|
|
778
864
|
|
|
779
865
|
**同一个 DeepSeek 账号,一条指令,让在线的 Windows 与 Mac 跑同一个项目。**
|
|
780
866
|
|
|
867
|
+
### 进度 —— 它现在到底到哪一步了
|
|
868
|
+
|
|
869
|
+
**当前版本:`0.41.0`**。自 `0.8.0` 以来共 **62 个版本**。比版本号更重要的数字是:
|
|
870
|
+
**11 个工具、79 个测试套件、2199 个用例、12 项预检、0 失败。**
|
|
871
|
+
|
|
872
|
+
#### 三项目标本来的属性
|
|
873
|
+
|
|
874
|
+
| 属性 | 状态 | 具体含义 |
|
|
875
|
+
|---|---|---|
|
|
876
|
+
| **分模块** | **已强制** | 每个文件属于且只属于一个已声明的层;出现未声明的依赖边,`layer-check` 直接失败 |
|
|
877
|
+
| **分层级** | **已强制** | 六个带序号的层,以及一张**允许清单**——不是一条可以被解读的规则,而是一张可以被读的清单 |
|
|
878
|
+
| **分权限** | **已建成,需手动开启** | 四个权限带、升级**恰好一级**、破坏性操作**永不自动放行**。**默认关闭**,见下方缺口 |
|
|
879
|
+
|
|
880
|
+
#### 路线图
|
|
881
|
+
|
|
882
|
+
| 版本 | 交付物 | 状态 |
|
|
883
|
+
|---|---|---|
|
|
884
|
+
| **v4.8.0** | 字段级接受、MMP P1/P2、lineage | **已交付** —— `w2m_memory` 接收来自其他机器的 CAT7 认知块,**按字段、按接收方的权限级别**逐项接受;接收方买不起的字段**是「缺席」,不是 null**;祖先无法产出时**拒绝,绝不重新生根** |
|
|
885
|
+
| **v4.9.0** | 审批队列 | **部分完成** —— 队列、升级桥、发起限流器都已接线,操作者可通过 `w2m_approvals` 处理。**但路线图里写的那道「前门」从未构建**:`approval-index.mjs` 只导入 `authority.mjs`,别无其他 |
|
|
886
|
+
| **v4.10.0** | 委员会治理 | **已交付** —— 七个阶段、加权投票、**平票是拒绝而不是打破**;封印的决定在产生它的委员会消失之后,**仍可连异议和异议的理由一起取回** |
|
|
887
|
+
|
|
888
|
+
#### 可达性检查发现了什么(这部分才是诚实的部分)
|
|
889
|
+
|
|
890
|
+
这一跨度里有**三个机制被完整实现、完整测试、却没有任何东西能到达**——`field-authority`、`approval-gate`、`council`。其中**两个在被描述为「已交付」时,没有任何东西在跑它们**。一条 `grep` 就推翻了其中一个。
|
|
891
|
+
|
|
892
|
+
现在有**门禁**了:一个没人能到达的模块会让预检**变红**,除非它出现在 `scripts/accepted-orphans.mjs` 里,**并写明理由和「什么能让它下榜」**。已经修好的条目若还留在清单上,同样失败。**目前还剩 7 个**:
|
|
893
|
+
|
|
894
|
+
| 模块 | 性质 | 什么能让它下榜 |
|
|
895
|
+
|---|---|---|
|
|
896
|
+
| `health.mjs` | **缺陷** | agent 按计划调用它 |
|
|
897
|
+
| `log-bounds.mjs` | **缺陷** | 同上——它是对**实测到的 40 倍日志增长**的答案 |
|
|
898
|
+
| `storage.mjs` | **缺陷** | 有工具真正通过它的「不可删除」层写入 |
|
|
899
|
+
| `fleet-scale.mjs`、`fleet-state.mjs`、`exit-channel.mjs`、`test-report.mjs` | 库 | 需要时给它们调用方 |
|
|
900
|
+
|
|
901
|
+
**「缺陷」这一类被单独命名,而不是和「库」混在一起**——因为仅凭导入图这两者无法区分,**只有写下来的理由能分开它们**。
|
|
902
|
+
|
|
903
|
+
#### 眼下最要紧的那个缺口
|
|
904
|
+
|
|
905
|
+
**本机实际运行的插件比这份源码落后 30 多个版本。** 在升级之前,**任何关于运行时行为的源码级结论都作废**。升级只需一条命令,见[快速开始](#快速开始)第 3 步。
|
|
906
|
+
|
|
907
|
+
#### 在真机上实测到的
|
|
908
|
+
|
|
909
|
+
| 事实 | 证据 |
|
|
910
|
+
|---|---|
|
|
911
|
+
| **七阶段委员会**能在 macOS 上跑通 | MacMini `exit 0`:7 个阶段、异议被保留、`recount` 复现成功 |
|
|
912
|
+
| **字段级接受**在 macOS 上成立 | MacMini `exit 0`:级别 0 拿不到 `evidence`、级别 70 拿得到、篡改被抓、**缺祖先时拒绝而不重新生根** |
|
|
913
|
+
| 编队是**5 台配对中 3 台可用** | 而 `fleet-scale.mjs` 因此**拒绝外推** |
|
|
914
|
+
| **`P2P_PUNCH_FAILED`** | **未验证。** 三个假设已测量并排除(BLAKE2s-256、更广的原语集、版本偏差)。**抛出它的是跑着 0.9.5 的插件**——升级就是下一个实验 |
|
|
915
|
+
|
|
916
|
+
#### 不作声明
|
|
917
|
+
|
|
918
|
+
**这不是 1.0.0。** `docs/1.0.0-ACCEPTANCE.md` 把全部 30 条准则按**一条规则**(而非观点)分成
|
|
919
|
+
**已强制 / 已测量 / 未验证**三类,列出**这一跨度不得不撤回的 7 条声明**,并明说它自己收尾清单里的四项尚未完成。它被一个测试**绑定到预检**,所以**一条准则不可能在文件仍声称它被强制的同时悄悄变成未被强制**。
|
|
920
|
+
|
|
781
921
|
### 为什么需要它
|
|
782
922
|
|
|
783
923
|
DSH 目前**不能**跨机器协作,而且不是疏忽:
|
|
@@ -817,8 +957,21 @@ node bin/w2m-localside.mjs --rabbit http://<中继地址>:8787 --pair PAIR-XXXXX
|
|
|
817
957
|
--allowed-commands '["node --test","git status --porcelain"]'
|
|
818
958
|
|
|
819
959
|
# 3) 装 DSH 插件
|
|
820
|
-
|
|
821
|
-
|
|
960
|
+
# v0.4.0 起可从 npm 装(比逐个指定 release 的 tgz 更好:不会锁死在某个旧版本上)。
|
|
961
|
+
# 本仓库自己的 profile 就一直锁在一个本地 0.9.5 的 tgz 上,结果落后了 30 多个版本。
|
|
962
|
+
npm install @twinsearth/w2m-dsh-plugin@latest
|
|
963
|
+
# 或者,如果你想钉死一个具体版本:
|
|
964
|
+
# npm install @twinsearth/w2m-dsh-plugin@0.41.0
|
|
965
|
+
#
|
|
966
|
+
# 没有 npm 的托管运行时(DSH 打包版就是这种)用 pnpm 即可,同一个 registry、同一份 tarball:
|
|
967
|
+
# pnpm add @twinsearth/w2m-dsh-plugin@0.41.0 --save-exact
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
装完后**重启 DSH**,然后确认版本对上了——这一步别跳过,因为**本仓库自己就在这里栽过**:
|
|
971
|
+
|
|
972
|
+
```
|
|
973
|
+
w2m_status → 应能看到 actor_authority 与 approvals 两个字段(0.9.5 没有)
|
|
974
|
+
工具列表 → 应能看到 w2m_council(旧版只有 8 个工具)
|
|
822
975
|
```
|
|
823
976
|
|
|
824
977
|
然后在 profile 的 `cordis.patch.yml` 里给插件 `rabbitUrl`(留空即用共享服务器)与 `operatorToken`,重启 DSH,即可直接用自然语言指挥:
|
package/docs/1.0.0-ACCEPTANCE.md
CHANGED
|
@@ -126,7 +126,7 @@ limiter down to `util`, **not** to add the edge.
|
|
|
126
126
|
| 3.7 | Nothing destructive auto-settles, at any level | `mayAutoSettle`, asserted | **yes** |
|
|
127
127
|
| 3.8 | An escalated dispatch reaches a queue an approver can answer | `w2m_approvals`, asserted end to end | **yes** |
|
|
128
128
|
| 3.9 | The queue's front door meters **raising only** | `approval-gate.mjs`, wired in 0.31.0 | **yes** |
|
|
129
|
-
| 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 |
|
|
130
130
|
| 3.11 | **The gate is ON by default** | `requireAuthorityLevel`, asserted | **NARROWED — see below** |
|
|
131
131
|
| 3.12 | A level is chosen by name, never typed | `authority-choice.mjs`, asserted | **yes** |
|
|
132
132
|
| 3.13 | A misconfigured level is reported, not silently ignored | `w2m_status.actor_authority` | **yes** |
|
|
@@ -181,7 +181,7 @@ within a week. **A check that cannot fail is a report.**
|
|
|
181
181
|
| 1.2 | every dependency edge is declared, and every declared edge is used | `layer-check` |
|
|
182
182
|
| 1.3 | every suite on disk is named by a script | `test wiring` |
|
|
183
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`** |
|
|
184
|
+
| **1.5** | **no orphan module without a written reason** | **`reachable or accepted`** — **7 orphans, 65 exports, down from 8 and 72** |
|
|
185
185
|
| 2.1–2.4 | ranked layers, an allow-list of edges, no upward edge | `layer-check` |
|
|
186
186
|
| 3.2 | every declared capability is reachable by some band | `auditCapabilities`, asserted |
|
|
187
187
|
| 3.4 | the dispatch path consults the gate | `tools.test.mjs`, end to end |
|
|
@@ -198,9 +198,9 @@ within a week. **A check that cannot fail is a report.**
|
|
|
198
198
|
|
|
199
199
|
| # | Criterion | What is actually measured |
|
|
200
200
|
|---|---|---|
|
|
201
|
-
| 1.6 | **reachability itself** |
|
|
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
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 |
|
|
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
204
|
| 3.11 | the gate is on by default | `requireAuthorityLevel` makes it enforceable **per machine**; the default is still off |
|
|
205
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
206
|
|
|
@@ -243,9 +243,12 @@ found something the previous three rounds had missed.**
|
|
|
243
243
|
|
|
244
244
|
1. **Flip §3.11's default.** `requireAuthorityLevel` exists and is tested; whether 1.0.0 ships with it on is a
|
|
245
245
|
decision about who the release is for. **A permission system that is off is one that has never run.**
|
|
246
|
-
2. **Wire the
|
|
247
|
-
name; `log-bounds` is the answer to a measured 40× growth; `storage` holds the undelatable
|
|
248
|
-
|
|
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.
|
|
249
252
|
3. **Re-run the 69 unverified suites on MacMini**, which needs `node_modules` there — an environment change
|
|
250
253
|
nobody has made.
|
|
251
254
|
4. **Promote every MEASURED row to ENFORCED**, so this document stops being a parallel list that can drift from
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.42.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",
|
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.42.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
|
{
|