@twinsearth/w2m-dsh-plugin 0.9.7 → 0.10.2
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 +273 -0
- package/ROADMAP.md +135 -0
- package/package.json +11 -4
- package/src/agent/agent.mjs +38 -32
- package/src/agent/approval-index.mjs +300 -0
- package/src/agent/authority.mjs +305 -0
- package/src/agent/blocks.mjs +426 -0
- package/src/agent/health.mjs +330 -0
- package/src/agent/storage.mjs +380 -0
- package/src/plugin/tools.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,279 @@ 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.10.2] — 2026-10-15
|
|
8
|
+
|
|
9
|
+
**Tiered storage — temporary, compressed, permanent, and the two kinds of knowledge that persist forever.**
|
|
10
|
+
|
|
11
|
+
### Why tiers rather than one directory
|
|
12
|
+
|
|
13
|
+
The Linux host reached 100% filesystem on 2026-10-14 from a single uncapped writer at 14.7 GB/day, and the
|
|
14
|
+
recovery cost real time. The lesson was not "delete more": it was that **data with different lifetimes was
|
|
15
|
+
stored in one place under one policy**, so the only available policy was "never delete" until it became
|
|
16
|
+
"delete under pressure" — which is the worst possible moment to be deciding what matters.
|
|
17
|
+
|
|
18
|
+
So a file's tier is written down when it is stored, and the tier alone decides what may happen to it:
|
|
19
|
+
|
|
20
|
+
| Tier | Lifetime | May it be deleted? |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| `temp` | a stated TTL | yes, automatically, once expired |
|
|
23
|
+
| `compressed` | until the archive is unwanted | only whole, never partially |
|
|
24
|
+
| `permanent` | forever | **no — refused, not warned about** |
|
|
25
|
+
| `skill` | forever | **no** |
|
|
26
|
+
| `law` | forever | **no — amended instead** |
|
|
27
|
+
|
|
28
|
+
### Why `permanent` refuses rather than warns
|
|
29
|
+
|
|
30
|
+
A warning is a thing a caller ignores, and the caller under pressure is exactly who this exists for.
|
|
31
|
+
`memory.jsonl` is the **only** durable copy of what a command printed — the relay keeps `stdout_sha256` and
|
|
32
|
+
never the text — so deleting it deletes the fleet's ability to answer what it already tried, and nothing
|
|
33
|
+
else in the system notices. `device.key` is the identity peers know a machine by; losing it makes every
|
|
34
|
+
peer holding the old one refuse that machine with no visible cause. A forced removal remains possible,
|
|
35
|
+
because a rule with no escape hatch gets bypassed rather than followed.
|
|
36
|
+
|
|
37
|
+
`law` entries carry an **amendment chain** naming the hash each amendment supersedes, because "what does
|
|
38
|
+
the rule say now" and "what did it used to say" are both questions a reader has, and only the second
|
|
39
|
+
explains why something was done the old way.
|
|
40
|
+
|
|
41
|
+
### A test that asserted nothing, found while writing it
|
|
42
|
+
|
|
43
|
+
The compressed-removal case ended with `existsSync(join(..., 'nope'))` — a path that never exists, so the
|
|
44
|
+
assertion could never be false. It now checks the real property: removal leaves no orphan on disk, because
|
|
45
|
+
an index that forgets a file still on disk is how a store grows without anything being able to name what
|
|
46
|
+
is in it.
|
|
47
|
+
|
|
48
|
+
**24 tests, 0 failures.**
|
|
49
|
+
|
|
50
|
+
## [0.10.1] — 2026-10-15
|
|
51
|
+
|
|
52
|
+
**A service-health agent: measure, classify, recommend — never act.**
|
|
53
|
+
|
|
54
|
+
### Why it only advises
|
|
55
|
+
|
|
56
|
+
A component that both decides a server is unhealthy *and* changes that server is one bug away from
|
|
57
|
+
deleting the thing it was protecting. The 2026-10-14 disk incident is the concrete example: the recovery
|
|
58
|
+
was `truncate`, **not** `rm`, because rsyslog still held the file descriptor open, so removing the file
|
|
59
|
+
would have freed nothing while appearing to succeed. A cleaner that had decided on its own to delete "the
|
|
60
|
+
big log file" would have looked successful and changed nothing.
|
|
61
|
+
|
|
62
|
+
### Where the thresholds come from — measurement, not taste
|
|
63
|
+
|
|
64
|
+
- **Disk** — critical at 90% used *or* under 1 GB free; warn at 80%. The host reached 100% with 268 MB
|
|
65
|
+
free, which is the state this is for. The absolute floor matters independently: a huge filesystem can be
|
|
66
|
+
"only 85% used" and still have less headroom than a task needs.
|
|
67
|
+
- **Log growth** — 14.7 GB/day is the measured rate that caused the incident. Any single file over 1 GB is
|
|
68
|
+
reported **by name**, because "the disk is filling" is not actionable and "this file is filling it" is.
|
|
69
|
+
- **Load** — compared against CPU count, never an absolute number, because the consequence worth naming is
|
|
70
|
+
that a loaded machine starts expiring tasks at the ~50 s lease ceiling.
|
|
71
|
+
- **Memory** — set where a host starts swapping, not where it looks comfortable.
|
|
72
|
+
|
|
73
|
+
### Two defects the suite found in its own work
|
|
74
|
+
|
|
75
|
+
- The critical load multiple was `loadPerCpuWarn * 2`, which **reads as a rule and is a coincidence** —
|
|
76
|
+
equal to 3.0 only while the warning threshold is 1.5, so changing one would silently move the other. Its
|
|
77
|
+
own test asserted the documented 1.5 / 3.0 pair and failed. It is now a named constant.
|
|
78
|
+
- A test asserted `critical` for 4.0 load over 2 CPUs. 4 / 2 is 2.0 per CPU, between warn and critical.
|
|
79
|
+
**The implementation was right and the assertion was not.**
|
|
80
|
+
|
|
81
|
+
### Permanent storage is a classification, not a copy
|
|
82
|
+
|
|
83
|
+
`DATA_CLASSES` marks `memory.jsonl`, `device.json`, `device.key` and `fleet-memory.md` as
|
|
84
|
+
`IRREPLACEABLE`. It copies nothing: deciding where data may live is a policy this module has no standing
|
|
85
|
+
to make, and the honest half is saying which data cannot be regenerated.
|
|
86
|
+
|
|
87
|
+
### A limitation measured rather than assumed
|
|
88
|
+
|
|
89
|
+
`df` exists on Linux and macOS and not on Windows, so a Windows sample carries `disks: []`. The important
|
|
90
|
+
part is that it is **empty** rather than filled with a healthy-looking default — an unmeasured disk must
|
|
91
|
+
never read as free space. A test asserts the empty case on whichever host runs the suite.
|
|
92
|
+
|
|
93
|
+
**20 tests, 0 failures.**
|
|
94
|
+
|
|
95
|
+
## [0.10.0] — 2026-10-15
|
|
96
|
+
|
|
97
|
+
**The cross-session approval index, and a syncer that refuses to wedge a repository.**
|
|
98
|
+
|
|
99
|
+
### The approval index (roadmap v4.9.0)
|
|
100
|
+
|
|
101
|
+
`approval-broker.mjs` answers approvals **per session**: a request belongs to one IM window and the
|
|
102
|
+
operator answers it there. That is right for the person in that conversation and wrong for a supervisor,
|
|
103
|
+
who by definition is not in it — the design's own words are that a supervisor reviews all agents' pending
|
|
104
|
+
approvals *from one queue*. Without this, a request raised in a session nobody is watching sits until its
|
|
105
|
+
deadline delegates it.
|
|
106
|
+
|
|
107
|
+
The index presents pending work across sessions, filtered by what the viewer may act on, and verifies
|
|
108
|
+
authority at the moment of decision. It decides nothing itself: `decide()` returns a verdict and the caller
|
|
109
|
+
settles the broker, because the broker already owns settlement and a second settler would be a second owner
|
|
110
|
+
of the same promise.
|
|
111
|
+
|
|
112
|
+
Two choices worth stating. The subject's authority level is recorded **at raise time**, because it is a
|
|
113
|
+
fact about who asked and must not be re-derived from a caller who might be the approver — an index that let
|
|
114
|
+
the approver supply it would let the approver lower it. And `listFor` returns work the viewer **cannot**
|
|
115
|
+
answer, with the reason, rather than filtering it out: a filtered list makes the fleet look idle at the
|
|
116
|
+
moment it is most blocked.
|
|
117
|
+
|
|
118
|
+
### `scripts/fleet-sync.mjs` — broadcast, sync, merge, with a decision
|
|
119
|
+
|
|
120
|
+
Three defects were found by running it, two of them dangerous in production:
|
|
121
|
+
|
|
122
|
+
- **A failed rebase wedged the repository.** No committer identity meant `git rebase` stopped part-way,
|
|
123
|
+
leaving a detached HEAD and `.git/rebase-merge` for a person to resolve. An automatic tool that can
|
|
124
|
+
wedge a repository is worse than one that does nothing. The identity is now checked before starting, and
|
|
125
|
+
a failure **aborts** the rebase to restore the exact previous state.
|
|
126
|
+
- **A failed `rev-parse` was read as a ref name.** `git rev-parse <unknown>` **echoes the argument** to
|
|
127
|
+
stdout and then exits non-zero, so a failed lookup produced `{ok:false, out:'origin/main'}` — a plausible
|
|
128
|
+
ref name attached to a status saying it does not exist. `inspect` read `out` alone and reported a
|
|
129
|
+
repository with **no remote at all** as `ahead` with `ahead/behind: NaN/0`. Every ref lookup now uses
|
|
130
|
+
`--verify`, and an unparseable `rev-list` is `unreadable`/stop rather than falling through to a default.
|
|
131
|
+
- **The published/unpublished safety test was dead code.** `published = local_only < ahead` can never be
|
|
132
|
+
true: `ahead` counts the commits absent from the remote-tracking branch and `--not --remotes` selects
|
|
133
|
+
exactly those commits. The real question is narrower — would this rebase rewrite a commit some remote ref
|
|
134
|
+
already reaches — and it is now answered by testing each local commit for reachability from the remotes.
|
|
135
|
+
|
|
136
|
+
A finding that changed a test rather than the code: **on a single upstream, a "both sides published"
|
|
137
|
+
divergence cannot exist.** If a local commit is reachable from `origin/main` then HEAD is its ancestor, so
|
|
138
|
+
`behind` is 0 and there is no divergence. Three fixtures failed before that became clear. The reachable
|
|
139
|
+
version of the hazard is a local commit some *other* remote ref already has — a branch pushed as a feature
|
|
140
|
+
and then rewritten — and that is what the test now builds.
|
|
141
|
+
|
|
142
|
+
**30 tests across the two, 0 failures**, against real repositories and a real bare remote: a mocked git
|
|
143
|
+
would have been a test of the mock.
|
|
144
|
+
|
|
145
|
+
## [0.9.9] — 2026-10-15
|
|
146
|
+
|
|
147
|
+
**Cognitive memory blocks (roadmap v4.8.0): field-level acceptance and lineage — and the first release whose suite was run on the fleet machine rather than on the development host.**
|
|
148
|
+
|
|
149
|
+
### `src/agent/blocks.mjs`
|
|
150
|
+
|
|
151
|
+
The memory layer shipped in 0.9.6/0.9.7 accepts a peer's claim **whole or not at all**, which is a blunt
|
|
152
|
+
instrument with two costs the roadmap records as P1 and P2. A block is now seven fixed fields so the
|
|
153
|
+
decision can be made per field:
|
|
154
|
+
|
|
155
|
+
| Field | May a receiver choose it? |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `subject`, `body`, `evidence` | yes |
|
|
158
|
+
| `confidence` | **no — refused whatever the chooser says** |
|
|
159
|
+
| `origin`, `observed_at`, `lineage` | **no — inherited, and setting them would be forging provenance** |
|
|
160
|
+
|
|
161
|
+
Lineage is by **content hash**, not by id, because an id is assigned by whoever creates the block and can
|
|
162
|
+
be copied. A block whose parent cannot be produced is **refused rather than re-rooted**: re-rooting turns
|
|
163
|
+
an unverifiable history into no history, which reads as a fresh, independent assertion, and that is how a
|
|
164
|
+
fabricated claim acquires a respectable past.
|
|
165
|
+
|
|
166
|
+
### Three defects the suite found in its own module
|
|
167
|
+
|
|
168
|
+
- **`confidence` was only a default, not an invariant.** Supplying a custom `choose` *replaced* the
|
|
169
|
+
default rather than refining it, so the file's central conservative property — a receiver does not adopt
|
|
170
|
+
the sender's certainty about the sender's own assertion — was silently optional. "Silently optional" is
|
|
171
|
+
how a safety default stops being one, so the refusal is now a hard invariant that no chooser can open.
|
|
172
|
+
- **A cycle fixture that asserted nothing.** The first cycle test mutated `lineage` after hashing and keyed
|
|
173
|
+
the map by the recomputed digest, leaving the references pointing at hashes the map did not hold — the
|
|
174
|
+
walker reported `missing` and never met the cycle it was supposed to test. `linkFixtures` now makes the
|
|
175
|
+
reference and the key the same string by construction.
|
|
176
|
+
- **A test that measured the clock.** `makeBlock` stamps the current time when no `observed_at` is given
|
|
177
|
+
(correctly — that is what the field means), so two blocks built a millisecond apart differ. The failure
|
|
178
|
+
looked like a hashing defect and was a 1 ms difference in a field the test never mentioned.
|
|
179
|
+
|
|
180
|
+
### Verified on MacMini, not only here
|
|
181
|
+
|
|
182
|
+
This is the first release whose suite was executed on the fleet machine:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
MacMini (MacMinideMac-mini.local)
|
|
186
|
+
HEAD : 642242446d3eb0ed1928f128d088b727284a6d43
|
|
187
|
+
expected : 642242446d3eb0ed1928f128d088b727284a6d43 MATCH
|
|
188
|
+
suiteOk : true
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`test/blocks.test.mjs` — **28 tests, 0 failures**, adversarial by design: most cases assert a refusal
|
|
192
|
+
rather than a happy path, because what makes a fleet memory worse than no memory is losing the ability to
|
|
193
|
+
tell a fact from an echo. `eslint` 0 errors.
|
|
194
|
+
|
|
195
|
+
### Notes on how this got here, because the tooling cost more than the module
|
|
196
|
+
|
|
197
|
+
`run-relay.mjs` targets `root@<relay>` and runs **bash on the relay host**; `relay-run.mjs` runs a
|
|
198
|
+
**JavaScript file on a fleet machine**. Both are called "relay" and only one of them is the fleet — a
|
|
199
|
+
clone believed to be on MacMini was on the relay for a round. Also: `Set-Content -Encoding utf8` writes a
|
|
200
|
+
**BOM**, and a shebang behind a BOM is not a shebang. And a 43 KB script cannot travel as an argv element
|
|
201
|
+
under Windows' 32 KB command-line limit, which is why this work reaches MacMini as a **branch** it fetches
|
|
202
|
+
rather than as an inline command.
|
|
203
|
+
|
|
204
|
+
### Environment, measured
|
|
205
|
+
|
|
206
|
+
MacMini has `git` 2.54.0, `curl`, `python3`, 70 GiB free, and **working** HTTPS to npm, the GitHub API,
|
|
207
|
+
release assets and codeload — including `git ls-remote`, which an earlier session recorded as failing.
|
|
208
|
+
It has no `npm`, `pnpm` or `brew`, so `node --test` directly is the suite runner.
|
|
209
|
+
|
|
210
|
+
## [0.9.8] — 2026-10-14
|
|
211
|
+
|
|
212
|
+
**The numeric authority model — the layer four of the five roadmap layers depend on — plus a roadmap and a memory write that reports its own outcome.**
|
|
213
|
+
|
|
214
|
+
### `ROADMAP.md`
|
|
215
|
+
|
|
216
|
+
The operator's architecture document specifies five layers across v4.7.0–v4.13.0. It lives in the
|
|
217
|
+
repository now, and it states the delta honestly before it states the plan: this repository has **one
|
|
218
|
+
relay and five machines**, against a target of 1,000+ agents. Every release row carries both a
|
|
219
|
+
deliverable and **the observation that would falsify it**, because a row without the second is a feature
|
|
220
|
+
list rather than a plan.
|
|
221
|
+
|
|
222
|
+
It also refuses three things on the record: it does not adopt a named protocol (InterMesh, A2A, NLIP,
|
|
223
|
+
AIX, MMP) before a line of its wire format exists; it does not build BFT consensus among five machines on
|
|
224
|
+
one relay, where it would add latency and failure modes without adding trust; and it does not specify
|
|
225
|
+
1,000-agent behaviour, because the source document's own §6.4 reports 30–50 agents as the practical limit
|
|
226
|
+
and superlinear "coordination collapse" beyond 100 — **a design not measured at 50 cannot honestly
|
|
227
|
+
specify 1,000.**
|
|
228
|
+
|
|
229
|
+
### `src/agent/authority.mjs` — v4.7.0
|
|
230
|
+
|
|
231
|
+
An integer `authority_level` on every actor, the four bands (0–49 ordinary, 50–69 captain, 70–89 org
|
|
232
|
+
admin, 90+ system admin), `matching_exempt_level` as the Unix-root-style exemption, and a decision
|
|
233
|
+
function returning `allow | needs_approval | deny` **with the rule that produced it**.
|
|
234
|
+
|
|
235
|
+
Why an integer rather than role names: authority appears in four of the five layers — permissions, the
|
|
236
|
+
approval flow, committee voting weights, and the defence hierarchy where a Coordinator outranks an
|
|
237
|
+
Analyst. An enumerated set of names would be re-partitioned by each layer, and every re-partition is a
|
|
238
|
+
place for two of them to disagree.
|
|
239
|
+
|
|
240
|
+
Three decisions are load-bearing and each is pinned by a test:
|
|
241
|
+
|
|
242
|
+
- **A rule with no `matching_exempt_level` exempts nobody.** The tempting default is "absent means
|
|
243
|
+
unconstrained", and it is wrong in the one direction that matters: a rule author who forgets the field
|
|
244
|
+
would silently create an unrestricted action.
|
|
245
|
+
- **An exemption bypasses membership, not the level floor.** The Unix analogy is exact — root bypasses a
|
|
246
|
+
file's permission bits, not the requirement that the file exists. A rule that says both is
|
|
247
|
+
self-contradictory, and the strict half wins.
|
|
248
|
+
- **A derived actor never exceeds its parent.** No rule in the source design grants delegation, and the
|
|
249
|
+
default an implementation reaches for — "a captain's delegate is a captain" — turns one compromised
|
|
250
|
+
actor into an escalation path. Where the source is silent, the narrow reading is the safe one.
|
|
251
|
+
|
|
252
|
+
### `evaluateAll` — a design corrected by its own test
|
|
253
|
+
|
|
254
|
+
The first draft evaluated every rule and returned the strictest verdict. A test asserting that the rules
|
|
255
|
+
after a gate are not evaluated failed, and the failure was right. `decisive` must be **unique**, or a
|
|
256
|
+
list can hold several non-allow decisions and whichever the caller picks, the *stated* reason for a
|
|
257
|
+
refusal stops being the reason. And a gate is a gate whether it says `deny` or `needs_approval`: in both
|
|
258
|
+
cases the caller's next action is determined and the remaining rules are decoration. It now stops at the
|
|
259
|
+
first non-allow.
|
|
260
|
+
|
|
261
|
+
### `envelope.memory` — a silent write that now reports
|
|
262
|
+
|
|
263
|
+
`appendMemory`'s result was deliberately unchecked, so a memory write could not fail a task. That remains
|
|
264
|
+
true — but leaving it entirely *silent* was the right call for the control flow and the wrong one for the
|
|
265
|
+
reader: **a machine whose memory is not being written is indistinguishable from one that has nothing to
|
|
266
|
+
remember**, which is precisely the class of invisible failure this layer exists to end. The envelope now
|
|
267
|
+
carries `memory: {written, file, rotated}` or `{written: false, reason_code}`. It is not a comparable
|
|
268
|
+
field, so no verdict moves and two machines cannot disagree over it.
|
|
269
|
+
|
|
270
|
+
### Verified
|
|
271
|
+
|
|
272
|
+
- `test/authority.test.mjs` — **27 tests, 0 failures.** Every one is about a boundary, because the only
|
|
273
|
+
interesting thing an authority model can get wrong is being off by one: a level too permissive is an
|
|
274
|
+
escalation and one too strict is an outage, so the band edges and the exemption boundary are asserted
|
|
275
|
+
**from both sides** rather than by a middle value that would pass either way.
|
|
276
|
+
- Band partition is proven total and non-overlapping across 0..100 by walking every level, so a seam that
|
|
277
|
+
belongs to two bands — a bug that only appears once someone is assigned that level — cannot exist.
|
|
278
|
+
- `eslint .` — 0 errors on the new module and test.
|
|
279
|
+
|
|
7
280
|
## [0.9.7] — 2026-10-14
|
|
8
281
|
|
|
9
282
|
**`w2m_memory` gains the collective layer and the one write a machine may not make for itself.**
|
package/ROADMAP.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Fleet roadmap — from a 5-machine fleet to a governed multi-agent system
|
|
2
|
+
|
|
3
|
+
Status: **plan, with one consumer already written.** This file exists so the roadmap lives in the
|
|
4
|
+
repository rather than in a conversation, and so each version's deliverable can be measured against
|
|
5
|
+
something rather than remembered.
|
|
6
|
+
|
|
7
|
+
Source: the operator's architecture document (2026-10-14), which specifies five layers — coordination,
|
|
8
|
+
memory, authority/scheduling, committee governance, and defence — across releases v4.7.0 to v4.13.0.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 0. What this repository already is, stated honestly
|
|
13
|
+
|
|
14
|
+
Before the roadmap, the delta, because a plan that does not say where it starts is a wish.
|
|
15
|
+
|
|
16
|
+
| Layer | This repository today | The document's target |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Networking | one relay, one fleet, five machines; P2P direct path when NAT allows | InterMesh/A2A/NATS-MQTT/libp2p/NLIP federation |
|
|
19
|
+
| Memory | **individual per-machine (shipped 0.9.6), collective curated document (0.9.7), conclusions (0.9.7)** | three-layer memory with field-level acceptance, lineage, consolidation and forgetting |
|
|
20
|
+
| Authority | one operator token; write scope; a global approval rate limiter | numeric `authority_level`, `matching_exempt_level`, a pending-approval index |
|
|
21
|
+
| Governance | none | AURA seven-phase council, weighted voting, recursive Sentinel/MetaSentinel |
|
|
22
|
+
| Scale | five machines, tasks in the hundreds | 1,000+ agents, sub-10 ms coordination, "coordination collapse" mitigation |
|
|
23
|
+
| Defence | hash-only ledger, HMAC signing, BFT-free consensus | three-tier sensor/analyst/coordinator with BFT |
|
|
24
|
+
|
|
25
|
+
**The gap is four to six orders of magnitude in agent count.** Nothing below claims otherwise, and the
|
|
26
|
+
roadmap is ordered so that what is built first is what the larger system would still need.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1. The one thing to build first, and why it is the only honest starting point
|
|
31
|
+
|
|
32
|
+
The document's own structure gives the answer: `authority_level` appears in **four of its five layers**.
|
|
33
|
+
It is in the permission model (§4.1), the approval flow (§4.2), the committee's weighted voting (§5.3,
|
|
34
|
+
weight includes authority) and the defence hierarchy (§2.3, where a Coordinator outranks an Analyst).
|
|
35
|
+
Nothing in the roadmap can be built without it, and building any of the rest first would mean inventing
|
|
36
|
+
authority implicitly — twice, differently.
|
|
37
|
+
|
|
38
|
+
**And it already has a consumer waiting.** §3.3's P1 is *field-level acceptance*: each agent decides
|
|
39
|
+
per field what to take from a peer, rather than accepting or rejecting a whole message. That is a
|
|
40
|
+
permission decision about a *field*, which is exactly what a numeric authority model expresses. The
|
|
41
|
+
memory layer shipped in 0.9.6/0.9.7 accepts everything or nothing today; `authority_level` is what turns
|
|
42
|
+
it into P1. So this is not a foundation poured in advance of a use — the use is already written.
|
|
43
|
+
|
|
44
|
+
## 2. Release plan
|
|
45
|
+
|
|
46
|
+
Each row states its **deliverable** and its **falsifying test** — the observation that would show it is
|
|
47
|
+
not working. A row without the second is a feature list, not a plan.
|
|
48
|
+
|
|
49
|
+
### v4.7.0 — authority model **shipped in 0.9.8**
|
|
50
|
+
|
|
51
|
+
**Deliverable.** `src/agent/authority.mjs`: an integer `authority_level` on every actor, the four bands
|
|
52
|
+
(0–49 ordinary, 50–69 captain, 70–89 org admin, 90+ system admin), a rule evaluator carrying
|
|
53
|
+
`matching_exempt_level`, and a decision function returning `allow | needs_approval | deny` with the rule
|
|
54
|
+
that produced it.
|
|
55
|
+
|
|
56
|
+
**Falsifying tests.**
|
|
57
|
+
- an actor at exactly `matching_exempt_level` bypasses the membership check, and one level below does not
|
|
58
|
+
(an off-by-one here is an escalation, so the boundary is tested from both sides);
|
|
59
|
+
- an actor never inherits another's level by acting on its behalf — delegation is not assumed, because
|
|
60
|
+
the document does not grant it and inventing it would be the worst possible default;
|
|
61
|
+
- a rule with no `matching_exempt_level` exempts nobody, rather than exempting everybody.
|
|
62
|
+
|
|
63
|
+
### v4.8.0 — memory: field-level acceptance (MMP P1/P2) **shipped in 0.9.9, suite verified on MacMini**
|
|
64
|
+
|
|
65
|
+
**Deliverable.** Extend the shipped memory layer: a CAT7 seven-field cognitive block, per-field
|
|
66
|
+
acceptance driven by §2's authority model, and lineage — each accepted claim records the content hash of
|
|
67
|
+
its parent, so a returned claim is identifiable as the receiver's own earlier thought.
|
|
68
|
+
|
|
69
|
+
**Falsifying tests.** a field refused by policy is *absent* from the stored block rather than null; a
|
|
70
|
+
claim's ancestor chain survives a round trip; a claim whose parent is missing is refused rather than
|
|
71
|
+
silently re-rooted.
|
|
72
|
+
|
|
73
|
+
### v4.9.0 — approval queue and scheduling
|
|
74
|
+
|
|
75
|
+
**Deliverable.** A cross-session pending-approval index (§4.2's "supervisor reviews every agent's pending
|
|
76
|
+
approvals from one queue"), with the already-shipped rate limiter as its front door. Scheduling stays
|
|
77
|
+
out of scope here — MARL is not buildable at five machines, and pretending otherwise would be the most
|
|
78
|
+
expensive kind of dishonesty in this roadmap.
|
|
79
|
+
|
|
80
|
+
### v4.10.0 — committee governance
|
|
81
|
+
|
|
82
|
+
**Deliverable.** The AURA six-role council as a *single* decision engine with the seven phases, weighted
|
|
83
|
+
voting whose weights come from v4.7.0's authority plus reputation, and **dissent retention** — the losing
|
|
84
|
+
arguments are stored, because §5.1's "preserves dissenting agents' arguments" is the property that makes
|
|
85
|
+
a decision auditable later.
|
|
86
|
+
|
|
87
|
+
**Falsifying tests.** a phase cannot be skipped; a vote's weights are reproducible from stored inputs; a
|
|
88
|
+
decision with recorded dissent is retrievable *with* the dissent.
|
|
89
|
+
|
|
90
|
+
### v4.11.0–v4.13.0 — scale, defence, integration
|
|
91
|
+
|
|
92
|
+
Named for completeness, not planned in detail, because planning them now would produce fiction. The
|
|
93
|
+
document's own §6.4 is the reason: it reports 30–50 agents as the practical limit of current frameworks
|
|
94
|
+
and describes superlinear "coordination collapse" beyond 100. **A design that has not been measured at 50
|
|
95
|
+
cannot honestly specify 1,000**, and the bitmask/sub-10 ms claims are targets to be tested, not facts to
|
|
96
|
+
be designed around.
|
|
97
|
+
|
|
98
|
+
## 3. What this roadmap deliberately refuses
|
|
99
|
+
|
|
100
|
+
- **It does not adopt a protocol by name.** InterMesh, A2A, NLIP, AIX and MMP are cited in the source
|
|
101
|
+
document; none is implemented here yet, and listing one as "chosen" before a line of its wire format
|
|
102
|
+
exists would be a claim no test could falsify. What is implementable now is the *semantics* they share
|
|
103
|
+
— identity, authority, field-level acceptance, lineage — and that is what v4.7.0 and v4.8.0 build.
|
|
104
|
+
- **It does not build consensus where there is nothing to agree on.** BFT among five machines on one
|
|
105
|
+
relay adds latency and failure modes without adding trust. It becomes meaningful when there are
|
|
106
|
+
mutually-distrusting organisations, which is a v4.12.0 precondition, not a v4.7.0 one.
|
|
107
|
+
- **It does not put a governance layer above a fleet that has no disagreements yet.** A council that
|
|
108
|
+
decides nothing is indistinguishable from a delay. The council earns its place when two fleets want the
|
|
109
|
+
same machine — and that is measurable, so it can wait for the measurement.
|
|
110
|
+
- **It does not scale the agent count by declaring it scaled.** §6.4's collapse is a real reported
|
|
111
|
+
phenomenon; the response here is a benchmark before a design, not the reverse.
|
|
112
|
+
|
|
113
|
+
## 4. Where the numbers in the source document are treated as claims, not facts
|
|
114
|
+
|
|
115
|
+
Several figures in the source (20–40% faster convergence, 45% storage reduction, 37% stability
|
|
116
|
+
improvement, 85–120 ms decision latency at 100+ agents, 35–42% cache hit rates, 35–78% token reduction)
|
|
117
|
+
come from cited literature on other systems. They are recorded as *expectations to test at this fleet's
|
|
118
|
+
scale*, and no roadmap item above is justified by them. The one number this repository has measured
|
|
119
|
+
itself — the ~50 s task ceiling, from `heartbeatIntervalMs × missedHeartbeats + leaseGraceMs` — is in
|
|
120
|
+
`fleet-memory.md` with its three measurements, and it is the reason every long job here is started
|
|
121
|
+
detached.
|
|
122
|
+
|
|
123
|
+
## 5. Relationship to the memory layer already shipped
|
|
124
|
+
|
|
125
|
+
The shipped layer is not a detour from this roadmap; it is §3.1's L2 and part of L3, built early because
|
|
126
|
+
it was cheap and the gap it closed had been paid for repeatedly. Concretely:
|
|
127
|
+
|
|
128
|
+
- `memory.jsonl` per machine = **L2 individual memory**, persisted, owned by the machine.
|
|
129
|
+
- `fleet-memory.md` on the archive host = **L3 collective memory**, curated rather than generated.
|
|
130
|
+
- `w2m_memory` = the retrieval entry point for both, plus `record` for conclusions.
|
|
131
|
+
- **L1 conversation memory** and §3.2's *emergent* collective memory (environment traces) are **not**
|
|
132
|
+
built.
|
|
133
|
+
|
|
134
|
+
The honest summary: the memory layer is at the point where the next thing it needs is authority, which is
|
|
135
|
+
why v4.7.0 is the next thing this roadmap builds.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.2",
|
|
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",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"README.md",
|
|
29
29
|
"CHANGELOG.md",
|
|
30
30
|
"HANDOFF.md",
|
|
31
|
+
"ROADMAP.md",
|
|
31
32
|
"LICENSE",
|
|
32
33
|
"PROTOCOL.md",
|
|
33
34
|
"PROTOCOL-v0.1.2.md",
|
|
@@ -111,13 +112,13 @@
|
|
|
111
112
|
}
|
|
112
113
|
},
|
|
113
114
|
"scripts": {
|
|
114
|
-
"test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.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",
|
|
115
|
+
"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/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",
|
|
115
116
|
"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",
|
|
116
117
|
"test:relay": "node --test --test-force-exit test/relay.test.mjs",
|
|
117
118
|
"test:agent": "node --test --test-force-exit test/agent.test.mjs test/memory.test.mjs",
|
|
118
119
|
"test:tools": "node --test --test-force-exit test/tools.test.mjs",
|
|
119
120
|
"test:crossnetwork": "node --test --test-force-exit test/crossnetwork.test.mjs",
|
|
120
|
-
"test:all": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.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",
|
|
121
|
+
"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/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",
|
|
121
122
|
"pack": "node scripts/pack.mjs",
|
|
122
123
|
"verify:join": "node scripts/verify-join.mjs",
|
|
123
124
|
"lint": "eslint .",
|
|
@@ -126,7 +127,13 @@
|
|
|
126
127
|
"format:check": "prettier --check .",
|
|
127
128
|
"verify": "node scripts/check-entrypoints.mjs && node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/tools.test.mjs test/e2e.test.mjs test/crossnetwork.test.mjs test/p2p-node.test.mjs test/p2p-agent.test.mjs test/client-bundle.test.mjs test/loader-entry.test.mjs",
|
|
128
129
|
"test:p2p": "node --test --test-force-exit test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/stun-server.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-agent.test.mjs",
|
|
129
|
-
"test:memory": "node --test --test-force-exit test/memory.test.mjs"
|
|
130
|
+
"test:memory": "node --test --test-force-exit test/memory.test.mjs",
|
|
131
|
+
"test:authority": "node --test --test-force-exit test/authority.test.mjs",
|
|
132
|
+
"test:blocks": "node --test --test-force-exit test/blocks.test.mjs",
|
|
133
|
+
"test:approval-index": "node --test --test-force-exit test/approval-index.test.mjs",
|
|
134
|
+
"test:fleet-sync": "node --test --test-force-exit test/fleet-sync.test.mjs",
|
|
135
|
+
"test:health": "node --test --test-force-exit test/health.test.mjs",
|
|
136
|
+
"test:storage": "node --test --test-force-exit test/storage.test.mjs"
|
|
130
137
|
},
|
|
131
138
|
"keywords": [
|
|
132
139
|
"dsh-bundle",
|