@junghanacs/entwurf 0.39.0 → 0.40.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/BASELINE.md +5 -3
- package/CHANGELOG.md +236 -0
- package/DELIVERY.md +34 -0
- package/README.md +11 -9
- package/VERIFY.md +11 -1
- package/docs/setup-clean-host.md +42 -3
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +19 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +10 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js +123 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js +259 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-transport.js +80 -0
- package/mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js +65 -0
- package/mcp/entwurf-bridge/src/index.ts +25 -5
- package/mcp/entwurf-bridge/tsconfig.build.json +7 -0
- package/package.json +2 -2
- package/pi-extensions/lib/meta-mailbox-body.ts +13 -4
- package/pi-extensions/lib/remote-pair-receive.ts +184 -0
- package/pi-extensions/lib/remote-pair-send.ts +325 -0
- package/pi-extensions/lib/remote-pair-transport.ts +81 -0
- package/run.sh +44 -0
- package/scripts/check-gate-qualification.ts +6 -0
- package/scripts/check-remote-pair-installed.ts +242 -0
- package/scripts/lib/remote-pair-installed-audit.ts +25 -0
- package/scripts/mutants/control-send-receipt.json +2 -2
- package/scripts/mutants/remote-pair-display.json +53 -0
- package/scripts/mutants/remote-pair-installed-audit.json +28 -0
- package/scripts/mutants/remote-pair-receive.json +126 -0
- package/scripts/mutants/remote-pair-roundtrip.json +83 -0
- package/scripts/mutants/remote-pair-send.json +194 -0
- package/scripts/mutants/remote-pair-transport.json +66 -0
- package/scripts/remote-pair-receive.ts +73 -0
package/BASELINE.md
CHANGED
|
@@ -46,8 +46,10 @@ outside the experimental preview (rows below).
|
|
|
46
46
|
| macOS physical-host probe | shipped, unrun on a Mac | `scripts/raw-macos-measure/` — `/bin/sh`, cells M1–M9, 3.5 s; needs neither entwurf install nor login. Still unmeasured: Darwin foreign-process environ (`ps -Eww`) and whether macOS `python3` is a CommandLineTools stub. |
|
|
47
47
|
| WSL2 Entwurf-only install + zero-token Pi citizen (GitHub Actions `windows-2025` runner, not a physical machine) | shape-only | Experimental, non-voting CI (not read by the release oracle): 2026-10-18, `.github/workflows/wsl2-install-surface.yml` run 39098773010 @ `b03d646` ([#78 receipt](https://github.com/junghan0611/entwurf/issues/78#issuecomment-6245947301)). Guest kernel `6.18.40.1-microsoft-standard-WSL2`, host `wsl --list --verbose` VERSION 2, guest fstype quoted raw as `ext2/ext3`. The run's packed tgz digest is equal across producer, host and guest; guest Node 24.18.1; harness-absent `setup` green with protected auth/harness state unchanged. Pi 1.3.0 (sha512 pinned to the registry integrity) wired by `setup`: one V3 record whose filename is its gid, a gid-keyed control socket with a native connect, `peer-facts` alive; only `get_state` was sent and no `agent_start` was observed. Pi creating an empty `auth.json` is not a login. It certifies no harness rail. Linux-guest runtime evidence only: not native-Windows evidence, and not a substitute for the required Linux CI jobs. |
|
|
48
48
|
| WSL2 delivery, non-pi harnesses, vendor turn, physical user machine | unverified | No receipt. Authentication and model turns were deliberately excluded from the CI cells' scope (not a CI impossibility); non-pi harnesses, delivery, fresh/resume and physical machines are unmeasured. |
|
|
49
|
-
| native Windows experimental preview: Pi citizen + `entwurf_v2` delivery (GitHub Actions `windows-2025` x64 runner, Node 24.18.1, Pi 1.3.0; 0.39.0
|
|
49
|
+
| native Windows experimental preview: Pi citizen + `entwurf_v2` delivery (GitHub Actions `windows-2025` x64 runner, Node 24.18.1, Pi 1.3.0; ships in core 0.39.0; the evidence below is the feature-branch CI artifact at `3b65bdc`, and the released tarball's own Windows cells are preview run 39146098592 @ `551402c`) | shape-only | Released tarball: preview run 39146098592 @ `551402c` installed the core 0.39.0 tarball (sha256 `1d5693241b8c…`) and its eight cells (L0, L0c, R1, R2, T1, N1, B1, D1) PASS, with a faux model and no vendor turn. The text that follows is the earlier feature-branch receipt (same addon sha256 `9837893521ec…`, not the release pack). Experimental, non-voting CI (not read by the release oracle): 2026-10-18, `.github/workflows/windows-native-preview.yml` run 39123555360 @ `3b65bdc`, with the required `ci.yml` four jobs green at the same SHA (run 39123555224). Build: MSVC (VS 2026, toolset v145, SDK 10.0.26100.0), two builds byte-identical (`entwurf_win.node` sha256 `9837893521ec…`), imports advapi32 + kernel32 with `node.exe` the only delay-load; node-gyp 13.1.0 and the Node headers/`node.lib` pinned. The pack carries exactly the addon and its manifest; npm installed it into a sandboxed consumer prefix; Pi came from a tarball checked against its pinned SRI. Six addon cells PASS (final-component reparse and non-regular refusals on the read handle, no connect through a symlink to a pipe, one-deadline timeout with reclaim, pipe-name stability and loader refusals). B1: two `pi --entwurf-control` citizens, one V3 record each, record-derived pipes alive through the product probe, `peer-facts` alive, graceful exit with both addresses dead afterwards. D1: A's model called `entwurf_v2` toward B's garden id with a nonce; A's receipt `control-socket → sent`, B's model context and B's transcript `entwurf-message` entry from A carry it. The model is an offline pi-ai `fauxProvider`: no vendor turn and no comprehension claim. The transcript fact is the harness's assertion; the raw transcript is not archived. The run used the elevated Administrators owner branch, so user-SID-only privacy is not measured. Pi's `peerDependencies` warning was observed, not waived. Not evidence for any other harness on Windows. |
|
|
50
50
|
| native Windows: own pre-existing pipe-name birth refusal, record link/rename syscalls, descendant cleanup after a forced kill, cross-user, mixed elevation, remote callers, user-SID-only privacy, physical user machine | unverified | No receipt; the preview cells name these as unmeasured. |
|
|
51
|
+
| Remote pair (#107): one declared pi-durable pair, Linux server ↔ Linux notebook over operator SSH (feature branch, not a release) | one LIVE exchange recorded (L4, one pair, one run) | 2026-10-20, both hosts at branch commit `e632294`; that commit's exact-SHA CI run 39202248407 was green on all four jobs. B's native `entwurf_v2` → one SSH → A `remote-pair-receive` → A mailbox enqueue `2026-10-19T22-19-47-662Z-13c897.msg`; A's one reply → B inbox read at `2026-10-19T22:20:24.837Z`, with the matching nonce `107-live-20261020T071937-B-A-6e42d1`. Before the send, the selected compiled bridge hash, the post-build bridge start and the record/owner/sender joins were checked on both hosts; the user reopened both visible sessions. Receipt: [#107](https://github.com/junghan0611/entwurf/issues/107#issuecomment-6278665895). |
|
|
52
|
+
| Remote pair: OFF target, negative paths over real SSH, other rails, more than one pair, non-Linux hosts, GUI rendering of the received envelope | unverified | No receipt. The deterministic and installed cells (VERIFY) do not stand for these. |
|
|
51
53
|
| native Windows: other harnesses, `setup` and the `.sh` bins, fresh/resume, mux/Herdr/ACP, ARM64, records over 16 MiB, reparse-backed store folders | UNSUPPORTED | Outside the preview's scope by decision; the preview loader refuses an install without the Windows addon by name. |
|
|
52
54
|
|
|
53
55
|
Notes the table cannot carry without becoming prose again:
|
|
@@ -69,8 +71,8 @@ Notes the table cannot carry without becoming prose again:
|
|
|
69
71
|
- **macOS evidence is split, not a support claim.** The Entwurf-only install surface is
|
|
70
72
|
CERTIFIED (CI) (row above); the harness rails are NOT CERTIFIED — pending
|
|
71
73
|
physical host (rental remains unscheduled). The package-level `os` field stays
|
|
72
|
-
unrestricted, and native Windows is an experimental preview (0.39.0
|
|
73
|
-
|
|
74
|
+
unrestricted, and native Windows is an experimental preview (core 0.39.0) with its own
|
|
75
|
+
named gaps (rows above), not a supported platform.
|
|
74
76
|
|
|
75
77
|
**Operator acceptance rule:** on a claimed Claude host, reinstall from the released
|
|
76
78
|
artifact, restart every old Claude process, open a new session, and run the doctor
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,242 @@ All notable changes to this project will be documented here. Format follows [Kee
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.40.0 - 2026-10-20
|
|
8
|
+
|
|
9
|
+
The bridge is crossed and the questions are carried forward. A person laid a bridge
|
|
10
|
+
between two machines, and a message sent across it to an existing sibling came back. That
|
|
11
|
+
crossing happened once, at pre-release branch commit `e632294`. This cut carries that
|
|
12
|
+
capability and its open questions, not a new two-host LIVE measurement at its own SHA. It
|
|
13
|
+
does not solve remote trust or remote visibility; the questions it leaves open are listed
|
|
14
|
+
below as questions, not as features.
|
|
15
|
+
|
|
16
|
+
### Remote pair (#107)
|
|
17
|
+
|
|
18
|
+
- **One operator-declared pair across two hosts.** An opt-in `entwurf-remote-pair.json`
|
|
19
|
+
beside each host's meta-record store has exactly four keys: `remoteGardenId`, `ssh` (an
|
|
20
|
+
existing alias), `localGardenId` and `remotePairFile` (the other host's own file). One
|
|
21
|
+
file serves both directions. An absent file is off. A symlink, non-regular, unreadable
|
|
22
|
+
or invalid file is refused. The file is read at every send and receive and never cached.
|
|
23
|
+
The operator owns the connection between the hosts (a private network such as Tailscale
|
|
24
|
+
is recommended, not required) and SSH authentication. Entwurf installs, configures,
|
|
25
|
+
discovers and retries none of it.
|
|
26
|
+
- **Send.** `entwurf_v2` to the declared `remoteGardenId` makes exactly one `ssh -o
|
|
27
|
+
BatchMode=yes -o ConnectTimeout=10 <alias> entwurf remote-pair-receive --pair <file>`
|
|
28
|
+
call, with no shell and the claim on stdin. A declared target that also has a local
|
|
29
|
+
trace is refused as `remote-route-local-conflict`. A sender other than `localGardenId`
|
|
30
|
+
is refused as `remote-sender-not-declared`. Both refusals make zero SSH calls. An
|
|
31
|
+
undeclared target takes the unchanged local path. The send ends in one of three ways.
|
|
32
|
+
The first is the other host's receipt, quoted verbatim: an acceptance, or an error it
|
|
33
|
+
reported while processing (`remote-reported-error`). The second is its named refusal
|
|
34
|
+
before any mutation. The third is `remote-indeterminate`. A result is decided only when
|
|
35
|
+
the exit code and JSON kind agree. There is no retry.
|
|
36
|
+
- **Receive.** `entwurf remote-pair-receive` accepts only its own canonical file path
|
|
37
|
+
(normalized, not realpath). It refuses a `from` or `target` outside the declared pair
|
|
38
|
+
before any mailbox mutation and hands a match to that host's existing local `entwurf_v2`
|
|
39
|
+
dispatch. The compiled twin ships in the tarball
|
|
40
|
+
(`dist/scripts/remote-pair-receive.js`). The received envelope names the remote origin,
|
|
41
|
+
marks the sender's cwd as a sender-host path and carries `replyable=false`, with "reply
|
|
42
|
+
route declared, not probed". This display is proven for the self-fetch mailbox body
|
|
43
|
+
only. `entwurf_callback` stays local.
|
|
44
|
+
- **Trust and visibility, as built.** The trust model is same-operator SSH account trust.
|
|
45
|
+
The receiving host checks the declared pair. It does not cryptographically authenticate
|
|
46
|
+
a citizen, and it says nothing about whether the content deserves trust. The route has
|
|
47
|
+
no launch, fresh or resume path. For the measured self-fetch pair an OFF citizen is
|
|
48
|
+
undeliverable, and every other target keeps its own rail's admission. The code does not
|
|
49
|
+
check that the target's screen is open; owner pids, TTYs and garden-id labels are not
|
|
50
|
+
visibility. Delivery to an active, armed pi-durable can start a turn at once.
|
|
51
|
+
- **A reported error is not read as non-delivery.** When the other host's dispatch reports
|
|
52
|
+
an error (exit 4), the sender's first line says that an error was reported, that
|
|
53
|
+
delivery is not inferred from that line and that nothing was retried, and it quotes the
|
|
54
|
+
other host's text verbatim. That text includes its own `retry-safe: false` after a
|
|
55
|
+
possible write. An external source review on #107 raised this before the cut.
|
|
56
|
+
- **Docs.** DELIVERY ("Remote pair (#107)") owns the contract. `docs/setup-clean-host.md`
|
|
57
|
+
gains the operator steps (§2c) and "Applying new source to a running host": a pi-durable
|
|
58
|
+
host runs the compiled bridge even from a checkout, and a running bridge keeps the
|
|
59
|
+
modules it loaded, so neither a pull nor a green setup line shows which bytes are live.
|
|
60
|
+
`docs/remote-sibling-principles.md` is new; it holds the boundaries kept across hosts
|
|
61
|
+
and the questions below.
|
|
62
|
+
|
|
63
|
+
### Questions carried forward
|
|
64
|
+
|
|
65
|
+
- **Is a connection the same as trust?** Today's boundary is an operator-owned SSH account
|
|
66
|
+
and one exactly declared pair. How should the reach of an account be told apart from
|
|
67
|
+
trusting the sibling that sent a message?
|
|
68
|
+
- **Does visibility survive a machine boundary?** Locally, the operator's visible-first
|
|
69
|
+
policy keeps conversation turns to siblings the user has open; that is a policy, not a
|
|
70
|
+
technical screen check. Across hosts, the first exchange relied on the user's own
|
|
71
|
+
confirmation that both sessions were open. What would observe and keep visible-first
|
|
72
|
+
remotely, without a hidden turn or a watcher?
|
|
73
|
+
- **How much can a sender know?** Acceptance is not a read, and a reply does not tell its
|
|
74
|
+
sender that the reply was read. How should that uncertainty be shown honestly without
|
|
75
|
+
retries, a third ACK, read tracking or a conversation manager?
|
|
76
|
+
|
|
77
|
+
None of these is a hidden backlog item;
|
|
78
|
+
[remote-sibling-principles](./docs/remote-sibling-principles.md) keeps them as open
|
|
79
|
+
questions. Automatic discovery, reconnection, OFF wake, hidden model turns, remote
|
|
80
|
+
callbacks and support for every backend or platform are not claimed.
|
|
81
|
+
|
|
82
|
+
### Evidence boundary
|
|
83
|
+
|
|
84
|
+
- **Deterministic and installed.** T1 executor-in and T2 surface-in cells sit beside the
|
|
85
|
+
behavior: the real bridge over MCP stdio, with a stand-in `ssh` running the other host's
|
|
86
|
+
real entry under a separate HOME. `./run.sh check-remote-pair-installed --tgz <abs>`
|
|
87
|
+
(checkout-only) runs T2 against installed bytes and audits module realpaths, with a
|
|
88
|
+
checkout negative control. Six new mutant lanes (remote-pair display 3, receive 8,
|
|
89
|
+
roundtrip 5, send 12, transport 4, installed-audit 2): the committed set is now 1034
|
|
90
|
+
mutants across 79 lanes. `check:vitest` runs `check-remote-pair-installed-audit`.
|
|
91
|
+
- **One real exchange, at `e632294`, not at this release's SHA.** On 2026-10-20 B's native
|
|
92
|
+
`entwurf_v2` went over one SSH call to A's mailbox
|
|
93
|
+
(`2026-10-19T22-19-47-662Z-13c897.msg`). A's one reply carried nonce
|
|
94
|
+
`107-live-20261020T071937-B-A-6e42d1`, and B read it at `2026-10-19T22:20:24.837Z`. The
|
|
95
|
+
pair was two already-open pi-durable siblings on two Linux hosts, which the user
|
|
96
|
+
reopened visibly beforehand. Before the send, the selected compiled bridge hash, the
|
|
97
|
+
bridge start time after the build and the record/owner/sender joins were checked on both
|
|
98
|
+
hosts. That check first caught one host still on a pre-feature build, so the trial was
|
|
99
|
+
held until a manual build and visible resume. This is L4 (one pair, one run). It is not
|
|
100
|
+
a release-gate MUST and was not re-measured at this release's SHA. Unmeasured: an OFF
|
|
101
|
+
target, negative paths over real SSH, other rails, more than one pair, non-Linux hosts,
|
|
102
|
+
GUI rendering, and SSH's own known-hosts side effects. Receipt:
|
|
103
|
+
[#107](https://github.com/junghan0611/entwurf/issues/107#issuecomment-6278665895).
|
|
104
|
+
- Kill-power re-measured at this shipped SHA: none (all 1034 committed claims not re-proved,
|
|
105
|
+
#144).
|
|
106
|
+
- Earlier-candidate PARTIAL (operator-attested, not this SHA): head 6ac477d944c6 surface
|
|
107
|
+
974e264f5879 -- `["bash","run.sh","check-tests-beside-behavior"]` 38/38 receipt
|
|
108
|
+
c8d59ba79b6a
|
|
109
|
+
- Earlier-candidate PARTIAL (operator-attested, not this SHA): head 3afbfb660562 surface
|
|
110
|
+
bc925701184b -- `["bash","run.sh","check-tests-beside-behavior"]` 37/37 receipt
|
|
111
|
+
4e324810643d
|
|
112
|
+
- Earlier-candidate PARTIAL (operator-attested, not this SHA): head 3afbfb660562 surface
|
|
113
|
+
bc925701184b -- `["bash","run.sh","check-remote-pair-installed-audit"]` 2/2 receipt
|
|
114
|
+
bfa5031abc54
|
|
115
|
+
- **An earlier RED is kept as history.** The `check-tests-beside-behavior` group at the R2
|
|
116
|
+
candidate (head a7aaac7390d8 surface 07625e040df9, receipt b5d681a291f5) killed 34 of
|
|
117
|
+
37: three mutants failed type-checking rather than the claim. Their replacements were
|
|
118
|
+
repaired and the group re-ran on the next candidate. No kill-power was carried over from
|
|
119
|
+
that run.
|
|
120
|
+
- **Static/LIVE split.** Local `check:full` is not run on this operator host and is not
|
|
121
|
+
counted as passed. The whole static floor is owed to this prepared SHA's exact push CI.
|
|
122
|
+
The earlier branch commit `e632294` passed all four jobs in run 39202248407; that is not
|
|
123
|
+
this SHA. The remote-pair installed gate is owed against this release's own candidate
|
|
124
|
+
tarball. The installed 11/11 on an earlier manually produced tarball is not this
|
|
125
|
+
release's evidence.
|
|
126
|
+
- **Local LIVE r1, this release's non-static LIVE: MUST (non-static 23) PASS=23 FAIL=0
|
|
127
|
+
SKIP=0 UNRUN=0; BEHAVIOR PASS=1 FAIL=0.** It ran once, 09:40:03–09:44:49 KST on
|
|
128
|
+
2026-10-20, on head `de0bf03` plus this preparation's four metadata files (diff
|
|
129
|
+
`22d0a8b856ae`), with the candidate fingerprint (`22053b62bc0e`) equal at freeze, before
|
|
130
|
+
and after. The one-off driver (`15d7fb9dd49f`) ran the release gate's non-static steps
|
|
131
|
+
in its own order, command, environment and working directory; its 24 step lines equal
|
|
132
|
+
`release_gate()`'s by kind, name and argv, and that body is byte-identical to 0.39.0's.
|
|
133
|
+
GLG approved this shape for this cut: test-owned scratch sessions only, including
|
|
134
|
+
headless model turns and the gates' own close/resume, with no existing user or OFF
|
|
135
|
+
session as a target
|
|
136
|
+
([#107](https://github.com/junghan0611/entwurf/issues/107#issuecomment-6280460022)). The
|
|
137
|
+
Codex step ran against a test-owned app-server in its own tmux session A (named pane pid
|
|
138
|
+
with its recorded start ticks, stopped by one TERM afterwards) from gate session S, with
|
|
139
|
+
the requested tiers `gpt-6-luna` and `openai-codex/gpt-6-luna`; that is the request, as
|
|
140
|
+
the step's own assertions check it, not a billing or model-identity proof. The compiled
|
|
141
|
+
bridge those steps spawned was rebuilt once beforehand, with GLG's approval, and matched
|
|
142
|
+
a private emit of this source 87/87. Receipts on the operator host:
|
|
143
|
+
`.tmp-verify/release-0.40.0/live-r1/` (`live.log` `4ced6b51ec03`, `steps.SHA256SUMS`
|
|
144
|
+
`f1b7e7b79d97`, 24 logs). It is not a local `release-gate --cut` PASS: the static step
|
|
145
|
+
did not run locally.
|
|
146
|
+
- **Still owed before the tag.** The static floor is this prepared SHA's exact push CI
|
|
147
|
+
(four required jobs). This release is carrier-bearing, so its candidate is the
|
|
148
|
+
native-Windows preview workflow's pack at the prepared SHA (release skill M3w), accepted
|
|
149
|
+
by the eight Windows cells and the Linux container, never a local `npm pack`; the
|
|
150
|
+
remote-pair installed gate runs against that same candidate. VERIFY §3.4 (the strict
|
|
151
|
+
`doctor-meta-bridge` on an installed 0.40.0 Claude host in a new post-install session)
|
|
152
|
+
follows the accepted candidate. Tag, GitHub release and npm publication each need their
|
|
153
|
+
own GO.
|
|
154
|
+
- **agy excluded from this cut.** VERIFY §3.7 (three agy doctors and a same-gid
|
|
155
|
+
native-push reply in a new operator conversation) is excluded by GLG's explicit decision
|
|
156
|
+
of 2026-10-20: unmeasured, not a PASS. It is a new decision for this cut only, not the
|
|
157
|
+
0.39.0 exclusion carried over and not a standing waiver
|
|
158
|
+
([#107](https://github.com/junghan0611/entwurf/issues/107#issuecomment-6280460022)).
|
|
159
|
+
- **Host residue after LIVE (P9).** The census counted 1934 directories owned by this
|
|
160
|
+
operator under `/tmp` after one stale, unused root of this repo's own prefix (16K) was
|
|
161
|
+
reclaimed, from 1935. The largest groups (`acp-cache-test`, `render-take-test`,
|
|
162
|
+
`caption-take-test`) do not match the prefixes this repo's gates mint, so their
|
|
163
|
+
ownership is not certified by that extractor; they were not deleted. One reparented
|
|
164
|
+
holder of a `/tmp` path is an unrelated editor daemon, untouched. Observation: the
|
|
165
|
+
memory-containment and chain gates left fresh roots of their own prefixes
|
|
166
|
+
(`entwurf-memguard-overlay-*`, `entwurf-chain-*`); that is a finding about those gates,
|
|
167
|
+
not fixed in this cut.
|
|
168
|
+
- **Herdr follows core.** `plugins/herdr/runtime-lock.json` returns from the published npm
|
|
169
|
+
0.39.0 pin to the closed `herdr-checkout` source for this unpublished candidate, as
|
|
170
|
+
0.38.1 and 0.39.0 did; the comment is re-dated only. The runtime-bootstrap gate requires
|
|
171
|
+
an npm-source lock to equal the checkout's package version, so an npm 0.39.0 pin cannot
|
|
172
|
+
sit beside package 0.40.0. There are no plugin manifest, registry pin or snapshot
|
|
173
|
+
changes; re-pinning the measured 0.40.0 registry bytes is a separate Herdr snapshot
|
|
174
|
+
after publication.
|
|
175
|
+
|
|
176
|
+
## v2026.10.19-herdr.0.39.0 — Herdr Entwurf plugin 0.39.0
|
|
177
|
+
|
|
178
|
+
A separate Herdr plugin snapshot, not another core release: core package 0.39.0 and its
|
|
179
|
+
`v0.39.0` tag remain unchanged. The operator has published the core to npm.
|
|
180
|
+
|
|
181
|
+
**Reading guide (2026-10-19).** The `## 0.39.0` section below and the appendix of the
|
|
182
|
+
[v0.39.0 GitHub release](https://github.com/junghan0611/entwurf/releases/tag/v0.39.0) are
|
|
183
|
+
prepare- and cut-time text. The appendix records the tag, VERIFY §3.4 met, and §3.7 (agy)
|
|
184
|
+
excluded for that cut only by the operator's decision, not a PASS. Both still say npm was not
|
|
185
|
+
published. The publish is the later registry fact recorded in this section (`latest` 0.39.0,
|
|
186
|
+
published 2026-10-18T21:24:39Z, registry bytes equal to the accepted candidate). Neither the
|
|
187
|
+
published tarball's CHANGELOG nor the release page is amended by this snapshot.
|
|
188
|
+
|
|
189
|
+
### Herdr plugin
|
|
190
|
+
|
|
191
|
+
- **Re-pin the published core 0.39.0 and close the checkout window.**
|
|
192
|
+
`plugins/herdr/runtime-lock.json` returns from the `herdr-checkout` carrier that core 0.39.0
|
|
193
|
+
rode to production npm source `@junghanacs/entwurf@0.39.0` with the registry's measured
|
|
194
|
+
sha512 (`sha512-xTRMQkSi…JY+1A==`). The plugin manifest moves 0.38.0 → 0.39.0; admission
|
|
195
|
+
remains Herdr >= 0.9.0 and the one-build/one-pane/Linux shape is unchanged. Core 0.38.1 got
|
|
196
|
+
no Herdr snapshot, so the previous npm pin is 0.38.0.
|
|
197
|
+
- **Pi range unchanged.** The acquired runtime still accepts Pi `>=1.3.0 <1.4`.
|
|
198
|
+
- **Upgrade by acquisition source, not just version.** A settled npm-source 0.38.0 installation
|
|
199
|
+
can rebind under the plugin README's ownership conditions. A host that installed an
|
|
200
|
+
intervening `herdr-checkout` candidate must explicitly deactivate by its own absolute runtime
|
|
201
|
+
path before reinstalling the npm-source snapshot. No operator installation or active session
|
|
202
|
+
is rewired by this release work.
|
|
203
|
+
|
|
204
|
+
### Source-specific verification
|
|
205
|
+
|
|
206
|
+
- Registry metadata reports `latest` 0.39.0 and `repair` 0.12.8-repair.1, 605 files, published
|
|
207
|
+
2026-10-18T21:24:39Z. The downloaded registry tarball is byte-equal to the preserved accepted
|
|
208
|
+
core candidate (SHA256 `1d5693241b8cd5fa827869cf15ecbf0048215c7f0b4ebb0422650b6ae2773578`,
|
|
209
|
+
11254616 bytes); its independently computed sha512 and sha1 equal `dist.integrity` and
|
|
210
|
+
`dist.shasum`, so core make's Docker consumer of those bytes (`node:24-bookworm`, repoDigest
|
|
211
|
+
`node@sha256:6dac556d…`) is reused here, not repeated. A sandboxed `pi install` of the
|
|
212
|
+
registry package resolved its bridge inside the sandbox, recorded the registry URL with that
|
|
213
|
+
integrity, and listed the curated Claude anchors with `--list-models entwurf`; a sandboxed
|
|
214
|
+
global npm install of the same package passed its installed `check-bridge`.
|
|
215
|
+
- **The npm runtime swap against the real registry**, by the registry-shipped
|
|
216
|
+
`scripts/herdr-runtime.mjs` (byte-equal to this checkout's) in sandboxed user roots, 22/22
|
|
217
|
+
checks. The 0.38.0 runtime was seeded from its own committed lock (`beb5925`); the swap to
|
|
218
|
+
0.39.0 took one acquisition, the cached tarball's sha512 equals the lock and its sha256 equals
|
|
219
|
+
both the ready journal's observed digest and the accepted candidate. The `installing` journal
|
|
220
|
+
carried the 0.38.0 identity as `previousRuntime` and the `runtime-ready` journal carries
|
|
221
|
+
`null`; when acquisition began the 0.38.0 tree was still at the active address. A manually
|
|
222
|
+
seeded torn swap (the active tree moved to the backup address with a half candidate in
|
|
223
|
+
staging; not a killed process) was restored before acquisition and reported `recovered:
|
|
224
|
+
torn-swap-with-candidate`. An exact re-request acquired nothing and left journal and tree
|
|
225
|
+
byte-identical, and the owned removal reclaimed cache, runtime and journal. The helper is a
|
|
226
|
+
leaf that probes no Pi, so these cells certify neither a Pi upgrade nor Herdr's
|
|
227
|
+
install/update relocation, harness wiring, the installed CLI's deactivate path, a model turn,
|
|
228
|
+
or recovery from a real kill.
|
|
229
|
+
- **Focused gates on the branch**, with HEAD, index and work surface unchanged across them:
|
|
230
|
+
`check-herdr-supply` (11 assertions), `check-herdr-plugin` (49),
|
|
231
|
+
`check-herdr-plugin-profile` (14), `check-herdr-runtime-bootstrap` (31),
|
|
232
|
+
`check-herdr-activation` (33), `check-herdr-placement` (25), `check-herdr-fresh-call` (45)
|
|
233
|
+
and `check-gate-manifests` (1000 committed mutants across 73 lanes validated, none executed).
|
|
234
|
+
`check-herdr-plugin-build` and `check-herdr-sandbox` were not run locally because they run
|
|
235
|
+
the operator's own herdr; they ride the pushed SHA's FULL floor in CI.
|
|
236
|
+
- Pending, and not claimed here: the pushed SHA's four-job CI and the public-ref
|
|
237
|
+
install/use/deactivate journey of this npm pin, on Herdr 0.9.5 (the binary
|
|
238
|
+
`scripts/fixtures/herdr-supply.json` names) with Pi 1.3.0, from the public `main` ref rather
|
|
239
|
+
than the tag. Earlier npm or checkout-carrier receipts cannot certify these source-specific
|
|
240
|
+
axes. Those outcomes are recorded in the snapshot's post-push acceptance ledger, not in this
|
|
241
|
+
frozen text.
|
|
242
|
+
|
|
7
243
|
## 0.39.0 - 2026-10-18
|
|
8
244
|
|
|
9
245
|
### Native Windows (experimental preview, #78)
|
package/DELIVERY.md
CHANGED
|
@@ -571,6 +571,40 @@ same class of capability through the CLI-owned stdio lifecycle. Absence of Claud
|
|
|
571
571
|
`FileChanged` / `asyncRewake` / `watchPaths` therefore means only that Claude's hook
|
|
572
572
|
mechanism cannot be copied — it never proved Copilot had no vendor wake surface.
|
|
573
573
|
|
|
574
|
+
### Remote pair (#107): one declared pair across two hosts
|
|
575
|
+
|
|
576
|
+
This is an opt-in route between two existing citizens on two hosts. Why it keeps these boundaries, and which questions it leaves open, is [remote-sibling-principles](./docs/remote-sibling-principles.md); this section owns the mechanism. It is off unless the operator places a file on each host. The operator owns the connection between the hosts (a private network such as Tailscale is recommended, not required) and owns SSH authentication and authorization. Entwurf installs, configures, discovers and retries none of it. It adds no relay, listener, queue or registry. One SSH process runs per send and then exits.
|
|
577
|
+
|
|
578
|
+
- **Declaration.** Each host has at most one `entwurf-remote-pair.json`, beside its meta-record store (`remotePairOptInPath()`: the pi agent dir, unless `ENTWURF_META_SESSIONS_DIR` moves the store and the file with it). It has exactly four keys:
|
|
579
|
+
- `remoteGardenId`.
|
|
580
|
+
- `ssh`: an existing SSH alias whose characters need no quoting.
|
|
581
|
+
- `localGardenId`.
|
|
582
|
+
- `remotePairFile`: the other host's canonical absolute path to its own file.
|
|
583
|
+
|
|
584
|
+
One file serves both directions. An absent file means off. A symlink, non-regular, unreadable or invalid file is refused. The file is read at every send and receive and never cached. The sending bridge and the SSH-spawned receive entry must resolve the same root; an override set in only one of those environments moves the file.
|
|
585
|
+
- **Send.** The sending host's `entwurf_v2` reads the file and then decides:
|
|
586
|
+
- A target other than `remoteGardenId` takes the unchanged local path.
|
|
587
|
+
- A declared target that also has a local trace on this host (a record in any state, or a control socket) is refused as `remote-route-local-conflict`, with zero SSH.
|
|
588
|
+
- A sender other than `localGardenId` is refused as `remote-sender-not-declared`, with zero SSH.
|
|
589
|
+
- Otherwise it spawns one `ssh -o BatchMode=yes -o ConnectTimeout=10 <alias> entwurf remote-pair-receive --pair <remotePairFile>`, with no shell and the claim on stdin.
|
|
590
|
+
- **Receive.** The entry accepts only when `--pair` equals its own canonical file path. Canonical means the normalized expected path, not realpath equivalence: a spelling through a symlinked home alias is refused. That file is the entry's whole scope. It refuses a `from` or `target` outside the declared pair before any mailbox mutation. A match goes to that host's existing local `entwurf_v2` dispatch, so every deliverability rule of the target's rail applies there; the measured pair used the self-fetch mailbox.
|
|
591
|
+
- **Outcomes.** The send ends in one of three outcomes:
|
|
592
|
+
- the other host's receipt, quoted verbatim (its paths are on the other host). This is either an acceptance or an error the other host reported while processing (`remote-reported-error`, exit 4). That error can follow a write, for example an enqueue whose later step threw and is reported `retry-safe: false`. The sender's header therefore claims neither non-delivery nor safety to resend; the quoted text is the fact;
|
|
593
|
+
- the other host's named refusal, made before any mutation: a pair mismatch (exit 3) or invalid input (exit 2);
|
|
594
|
+
- `remote-indeterminate`.
|
|
595
|
+
|
|
596
|
+
A result is decided only when the exit code and the JSON kind agree. There is no retry. An unanswered message stays unconfirmed. Acceptance is not a read or a reply. Neither side learns whether its last message was read unless the other side answers, and there is no third ACK.
|
|
597
|
+
- **Display.** The received envelope names the remote origin and route, marks the sender's cwd as a sender-host path, and carries `replyable=false`, with "reply route declared, not probed" stated separately. Display is proven only for the self-fetch mailbox body; control-socket and native-push rendering are not proven. `entwurf_callback` stays local.
|
|
598
|
+
- **Trust.** The model is same-operator SSH account trust. The receiving host checks the declared pair. It does not cryptographically authenticate a citizen, and the same account could claim either declared id.
|
|
599
|
+
- **Visibility.** For the measured self-fetch pair, delivery requires backing records, live owners and armed watches, so an OFF citizen of that rail is undeliverable. Any other target keeps its own rail-specific local admission. The remote route has no launch, fresh or resume path. The code does not check that the target's screen is open: an owner pid, a TTY or a garden-id label is not visibility. Live use addresses only siblings the user has open, and delivery to an active pi-durable can start a turn at once.
|
|
600
|
+
- **SSH side effects.** The transport adds exactly two options, `BatchMode=yes` and `ConnectTimeout=10`. Host-key checking, known-hosts writes and every other SSH policy remain the operator's, as configured. Their side effects on a real exchange were not measured.
|
|
601
|
+
|
|
602
|
+
Evidence:
|
|
603
|
+
|
|
604
|
+
- T1 (executor-in) and T2 (surface-in) are deterministic. T2 drives the real bridge over MCP stdio, and a stand-in `ssh` on PATH runs the other host's real entry under a separate HOME. An installed-tarball gate runs the T2 cells against installed bytes.
|
|
605
|
+
- One real exchange is recorded in [#107](https://github.com/junghan0611/entwurf/issues/107#issuecomment-6278665895), at branch commit `e632294` on 2026-10-20. A nonce went B→A→B between two already-open pi-durable siblings on two Linux hosts. That is L4: one pair, one run.
|
|
606
|
+
- Unmeasured: an OFF target, negative paths over real SSH, other rails, more than one pair, and non-Linux hosts.
|
|
607
|
+
|
|
574
608
|
## Recording a new claim
|
|
575
609
|
|
|
576
610
|
For every matrix change, record:
|
package/README.md
CHANGED
|
@@ -81,11 +81,10 @@ Detected harness wiring on Darwin can therefore be written while setup stays
|
|
|
81
81
|
non-green. WSL2: a CI runner (experimental) measured the Entwurf-only install and a
|
|
82
82
|
zero-token Pi citizen; general, physical-machine or harness-parity WSL2 support is **not
|
|
83
83
|
declared**, and delivery, other harnesses and physical machines are unmeasured. Native
|
|
84
|
-
Windows has an **experimental first preview**, a CI-built x64 addon
|
|
85
|
-
0.39.0
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
two Pi citizens and
|
|
84
|
+
Windows has an **experimental first preview**, a CI-built x64 addon that ships in core
|
|
85
|
+
0.39.0 (published to npm); 0.38.1 and earlier carry no Windows addon. On a GitHub Actions
|
|
86
|
+
`windows-2025` x64 runner with Node 24.18.1 and Pi 1.3.0, a CI-built artifact was installed,
|
|
87
|
+
wired with `pi install`, birthed two Pi citizens and
|
|
89
88
|
delivered one garden-id `entwurf_v2` message between them, with an offline scripted model
|
|
90
89
|
and no vendor turn. Every other native-Windows surface (other harnesses, `setup` and the
|
|
91
90
|
`.sh` bins, fresh/resume, mux/Herdr/ACP, physical machines) is **not declared**; the
|
|
@@ -198,9 +197,9 @@ pi / pi-durable / Claude Code / Copilot / OMP / Antigravity / Codex
|
|
|
198
197
|
| Open a new visible sibling | `entwurf_fresh_call {backend, model, task, cwd?}` | Launch receipt only; the sibling's nonce callback envelope supplies its new garden id |
|
|
199
198
|
| Reopen a dormant pi citizen under its own id | `entwurf_resume_call {target}` | Separate LAUNCH and socket OBSERVATION receipts; no turn is run |
|
|
200
199
|
|
|
201
|
-
**Delivery starts no process.** A live pi receives over its record-addressed
|
|
202
|
-
|
|
203
|
-
through its mailbox; a probe-alive native-push citizen (Antigravity, Codex) through
|
|
200
|
+
**Delivery starts no process.** A live pi receives over its record-addressed control
|
|
201
|
+
address (a Unix socket; on the experimental native-Windows Pi preview, a named pipe);
|
|
202
|
+
an armed self-fetch citizen (Claude Code, Copilot, OMP, pi-durable) through its mailbox; a probe-alive native-push citizen (Antigravity, Codex) through
|
|
204
203
|
its native conversation/thread. Dormant pi, undeliverable mailboxes, dead/indeterminate native
|
|
205
204
|
push and record-less sockets reject honestly. Only control-socket dispatch takes
|
|
206
205
|
the per-target lock; the other rails use their own deliverability/probe evidence.
|
|
@@ -213,6 +212,8 @@ receipt. `wants_reply` is etiquette, not waiting, polling or ownership; replyabi
|
|
|
213
212
|
is a live rail fact, not merely a consequence of trusted identity.
|
|
214
213
|
[DELIVERY](./DELIVERY.md#control-socket-acceptance-boundaries-120) owns these distinctions.
|
|
215
214
|
|
|
215
|
+
**One declared remote pair (opt-in, #107).** The operator places an `entwurf-remote-pair.json` on each of two hosts. Then `entwurf_v2` to the declared remote garden id runs the other host's `entwurf remote-pair-receive` once over the operator's own SSH, and that host checks the pair and dispatches locally. The trust is the same operator's SSH account. It makes one attempt with no retry, and it uses no relay or listener. [DELIVERY](./DELIVERY.md#remote-pair-107-one-declared-pair-across-two-hosts) owns the contract; [setup](./docs/setup-clean-host.md#2c-optional-remote-pair-between-two-hosts-107) covers the operator steps.
|
|
216
|
+
|
|
216
217
|
**Mux launches; it never delivers.** Inside Herdr, fresh opens a new unfocused tab
|
|
217
218
|
in the caller's workspace, pi/Claude Code only, with no tmux fallback. Outside,
|
|
218
219
|
it opens pi/pi-durable/Claude Code/Copilot/OMP/Codex visibly on the caller's own tmux
|
|
@@ -238,7 +239,8 @@ and [Herdr launch](https://github.com/junghan0611/entwurf/blob/main/docs/herdr-l
|
|
|
238
239
|
|
|
239
240
|
The pi adapter registers the compiled bridge with Pi's built-in MCP after record
|
|
240
241
|
birth; Pi supervises the MCP child. **MCP is ingress; pi receive remains the
|
|
241
|
-
record-addressed
|
|
242
|
+
record-addressed control address (a Unix socket, or the native-Windows preview's named
|
|
243
|
+
pipe).** Garden identity and dispatch stay Entwurf's.
|
|
242
244
|
Codemode is harness-internal, not another garden citizen.
|
|
243
245
|
|
|
244
246
|
## Operation guides
|
package/VERIFY.md
CHANGED
|
@@ -209,6 +209,16 @@ that moved out of `scripts/` and into a file beside its subject would otherwise
|
|
|
209
209
|
|
|
210
210
|
Do not collapse source, package, fixture, and native-host evidence into one “green.”
|
|
211
211
|
|
|
212
|
+
**Remote pair (#107).** Each layer of evidence lives in its own place:
|
|
213
|
+
|
|
214
|
+
- **Deterministic.** The cells sit beside the behavior in `pi-extensions/lib/remote-pair-*.test.ts` and are found by `check-tests-beside-behavior`. T1 is executor-in. T2 is surface-in: the real bridge over MCP stdio, with a stand-in `ssh` on PATH that runs the other host's real `run.sh` under a separate HOME. Both are offline.
|
|
215
|
+
- **Installed bytes.** `./run.sh check-remote-pair-installed --tgz <abs>` is checkout-only and takes a caller-supplied candidate tarball. It installs a sandboxed copy, checks the compiled surface, and runs the T2 cells against the installed subject. It also audits that every module those processes load resolves inside the consumer prefix, with a checkout negative control. Its audit primitive is mutation-qualified as `check-remote-pair-installed-audit` inside `check:vitest`.
|
|
216
|
+
- **Real two-host exchange (T3).** This is opt-in operator LIVE evidence, recorded in [BASELINE](./BASELINE.md) and #107. It is not a release-gate MUST and not a fixture that needs a second machine online. A T1, T2 or installed green does not stand for it, and its record belongs to the commit it ran on; a later SHA does not inherit it. Re-run T3 when any of these change:
|
|
217
|
+
- the SSH argv or quoting;
|
|
218
|
+
- the receive entry's layout, stdout or exit codes;
|
|
219
|
+
- the declaration schema or root resolution;
|
|
220
|
+
- either host's Node or Pi.
|
|
221
|
+
|
|
212
222
|
**Native durable contact (#129, #130 carrier).** Three deterministic beside-test/mutant
|
|
213
223
|
coordinates in `check:contracts` split the durable contact by the file that carries each claim's QK
|
|
214
224
|
(#144): a carrier or native-module mutant runs its one file, the rest run four. `check-pi-durable-beside` holds the adapter, the installed
|
|
@@ -428,7 +438,7 @@ mailbox with `origin=pi-session`, `replyable=true`).
|
|
|
428
438
|
|
|
429
439
|
### 1.4 Cross-install / cross-backend parity (optional, high-value)
|
|
430
440
|
|
|
431
|
-
Compare a fresh self-awareness report across axes: (1) same backend, different install path — answer must be path-invariant; (2) same backend, different machine — identical native tool list + MCP server/tool set; (3) different backend, same bridge — same garden capability but **different** native tool surface (a Claude session reporting another backend's native tools is a fail); (4) native pi routing vs ACP-bridged, same model — the native pi citizen reaches the compiled `entwurf-bridge` through Pi's built-in MCP (`mcp__entwurf_bridge__<verb>`, six active tools and two hidden), while the ACP backend reaches that bridge through the explicit `entwurfProvider.mcpServers` projection in its own callable dialect (see §2.1). Compare the actual ingress provenance and backend-native tools, not an assumed absence of MCP on pi; receive to the pi citizen remains its record-addressed Unix
|
|
441
|
+
Compare a fresh self-awareness report across axes: (1) same backend, different install path — answer must be path-invariant; (2) same backend, different machine — identical native tool list + MCP server/tool set; (3) different backend, same bridge — same garden capability but **different** native tool surface (a Claude session reporting another backend's native tools is a fail); (4) native pi routing vs ACP-bridged, same model — the native pi citizen reaches the compiled `entwurf-bridge` through Pi's built-in MCP (`mcp__entwurf_bridge__<verb>`, six active tools and two hidden), while the ACP backend reaches that bridge through the explicit `entwurfProvider.mcpServers` projection in its own callable dialect (see §2.1). Compare the actual ingress provenance and backend-native tools, not an assumed absence of MCP on pi; receive to the pi citizen remains its record-addressed control address (a Unix socket; a named pipe on the experimental native-Windows Pi preview). Honest "I cannot tell" hedging is preferable to inventing provenance. This is the current #125 comparison criterion, not a newly measured parity receipt. agy is graded by its native-citizen checklist, not by pretending it has an ACP overlay.
|
|
432
442
|
|
|
433
443
|
**Cortex on axis 3.** A cortex session must expose the same garden capability
|
|
434
444
|
while keeping cortex's own native tool surface. Its callable MCP identifier shape is
|
package/docs/setup-clean-host.md
CHANGED
|
@@ -19,8 +19,7 @@ evidence split, and its three states must not collapse into "macOS is supported"
|
|
|
19
19
|
unmeasured Darwin doctor reason is per-process environment DISCOVERY
|
|
20
20
|
(start-key is portable).
|
|
21
21
|
- **UNSUPPORTED** — native Windows `setup` and its harness rails. macOS is never this.
|
|
22
|
-
An experimental native-Windows Pi preview (0.39.0
|
|
23
|
-
and npm publication pending) exists outside `setup`; see [BASELINE](../BASELINE.md#release-host-baseline).
|
|
22
|
+
An experimental native-Windows Pi preview (core 0.39.0) exists outside `setup`; see [BASELINE](../BASELINE.md#release-host-baseline).
|
|
24
23
|
|
|
25
24
|
## Requirements
|
|
26
25
|
|
|
@@ -244,7 +243,8 @@ message. These are harness tools, not shell subcommands; pi exposes them through
|
|
|
244
243
|
Pi's built-in MCP as `mcp__entwurf_bridge__<verb>`. The bridge has eight tools;
|
|
245
244
|
pi hides `entwurf_inbox_read` and `entwurf_register_native`, leaving six active.
|
|
246
245
|
Tool-call ingress is MCP; delivery to a pi citizen is still its record-addressed
|
|
247
|
-
Unix
|
|
246
|
+
control address (a Unix socket; a named pipe on the experimental native-Windows Pi
|
|
247
|
+
preview). A boot/listing from `check-bridge` is not a model-turn or
|
|
248
248
|
native-rail acceptance.
|
|
249
249
|
|
|
250
250
|
To inspect the ACP provider from a project-local package instead of a global one:
|
|
@@ -449,6 +449,34 @@ The owned source is formatted at upstream width (`biome.json` override, indentWi
|
|
|
449
449
|
per-file exception. A formatting change to an owned file changes its emitted bytes. So it is
|
|
450
450
|
re-emitted with `--write`, and its generated digest is reviewed like any other carrier change.
|
|
451
451
|
|
|
452
|
+
## 2c. Optional remote pair between two hosts (#107)
|
|
453
|
+
|
|
454
|
+
This lets one declared pair of existing citizens on two hosts reach each other over SSH that you already run. [DELIVERY](../DELIVERY.md#remote-pair-107-one-declared-pair-across-two-hosts) owns the contract. The machine boundary is set by a person, and nothing here is automatic.
|
|
455
|
+
|
|
456
|
+
1. **Connect the hosts yourself.** Use a private network (Tailscale or similar is recommended) and SSH keys and authorization you already trust. From each host, `ssh -o BatchMode=yes <alias> true` must succeed non-interactively, and the other host's non-interactive `command -v entwurf` must resolve to the installation you intend. Entwurf edits no SSH config, key, known-hosts file or network setting. Whatever SSH itself writes under your own policy, such as host-key updates, stays yours.
|
|
457
|
+
2. **Run the accepted bytes on both hosts, in the processes that matter.** See [applying new source to a running host](#applying-new-source-to-a-running-host). A pulled checkout or a green setup line does not, by itself, show what a running bridge loaded.
|
|
458
|
+
3. **Write one file on each host,** at `remotePairOptInPath()`. That is the pi agent dir unless `ENTWURF_META_SESSIONS_DIR` moves the meta-record store. Check that the sending bridge's environment and the SSH receive environment resolve the same path. Use mode `0600` and never overwrite a file you did not inspect:
|
|
459
|
+
|
|
460
|
+
```json
|
|
461
|
+
{
|
|
462
|
+
"remoteGardenId": "<the other host's garden id>",
|
|
463
|
+
"ssh": "<your existing alias for the other host>",
|
|
464
|
+
"localGardenId": "<this host's garden id that talks to it>",
|
|
465
|
+
"remotePairFile": "<absolute path of the other host's own file>"
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
The two files mirror each other: one host's `remoteGardenId` is the other's `localGardenId`. The other host's `remotePairFile` must spell this path as it is normalized, not through a symlinked alias.
|
|
470
|
+
|
|
471
|
+
To check a file, read it with the repository's internal parser from a source checkout. Use the same agent/store-root settings the bridge runs with. This is a read-only dev-clone check: there is no installed operator validation verb, and it performs no send and no model input.
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
node --experimental-strip-types -e 'import("./pi-extensions/lib/remote-pair-send.ts").then(m=>console.log(m.readRemotePairOptIn(m.remotePairOptInPath())))'
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Do not validate the configuration by sending a test message.
|
|
478
|
+
4. **Use it only between siblings you have open.** The measured self-fetch pair must be active and armed, and the target rail's own admission still applies. Delivery never starts, resumes or wakes an OFF citizen. To an active, armed one it rings the normal doorbell, which may start a turn at once in the receiving TUI. Remove the file to switch the pair off; there is nothing to stop or reconnect.
|
|
479
|
+
|
|
452
480
|
## 3. Optional Claude Code native citizen
|
|
453
481
|
|
|
454
482
|
This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending physical
|
|
@@ -1128,6 +1156,17 @@ run `pnpm install` when the lockfile changed and `pnpm run build-bridge` before
|
|
|
1128
1156
|
artifact-consuming checks such as `./run.sh check-bridge-delivery`; ignored dist
|
|
1129
1157
|
may be missing or stale. Installed mode never bootstraps pnpm in `node_modules`.
|
|
1130
1158
|
|
|
1159
|
+
### Applying new source to a running host
|
|
1160
|
+
|
|
1161
|
+
New source on disk is not a fresh runtime. A pi-durable host spawns the compiled bridge `mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js` even from a checkout (the bootstrap's `PACKAGED_BRIDGE_ENTRY`). Ordinary `start.sh` in a checkout runs the TypeScript source instead. An already-running bridge keeps the modules it loaded. So, for a host that has to run new bridge code:
|
|
1162
|
+
|
|
1163
|
+
1. Build once from the checkout (`pnpm run build-bridge`, or its body `bash scripts/with-dist-lock.sh bash scripts/build-bridge.sh` when no package lifecycle should run).
|
|
1164
|
+
2. Compare the whole compiled output with the accepted candidate's, as a file-set and hash manifest. A hash of the selected entry alone is not enough: the entry can stay identical while a helper it loads differs. Check the pinned SDK set separately, and that the new process started after the build.
|
|
1165
|
+
3. Close the visible session and reopen it with its own native same-id resume.
|
|
1166
|
+
4. Confirm `entwurf_self` names the same garden id, and that the receiver/owner join points at the new process, whose bridge child started after the build.
|
|
1167
|
+
|
|
1168
|
+
A per-component setup PASS or a `git pull` does not show which bytes a running bridge loaded. On 2026-10-20 one host of the #107 pair still selected a pre-feature dist after a pull and a green setup. The cause was not traced. A hash check caught it before the first message, and a manual build and visible resume fixed it. Entwurf adds no auto-build, reload or watcher for this.
|
|
1169
|
+
|
|
1131
1170
|
## 8. Release acceptance versus host acceptance
|
|
1132
1171
|
|
|
1133
1172
|
- `entwurf check-bridge`: installed MCP bytes boot; no backend auth.
|
|
@@ -84,6 +84,9 @@ import { probeNativeSenderAlive, reconcileSenderIdentityClaims, resolveCodexRequ
|
|
|
84
84
|
import { applyOmpBridgeChildRootPolicy, defaultMetaMailboxDir, defaultMetaSessionsDir, makeStoreRecordReader, readActiveStoreEntries, readMetaIdentityByGardenId, readMetaInbox, readMetaReceiverMarker, readMetaSenderMarker, requireBackend, } from "../../../pi-extensions/lib/meta-session.js";
|
|
85
85
|
import { RESUME_CALL_REJECT_HINT, resumeCall } from "../../../pi-extensions/lib/mux-resume-call.js";
|
|
86
86
|
import { registerNativeConversation } from "../../../pi-extensions/lib/native-push/register.js";
|
|
87
|
+
import { makeLocalTraceFact, readRemotePairOptIn, remotePairOptInPath, runEntwurfV2WithRemoteRoute, } from "../../../pi-extensions/lib/remote-pair-send.js";
|
|
88
|
+
import { processTransport } from "../../../pi-extensions/lib/remote-pair-transport.js";
|
|
89
|
+
import { CONTROL_SOCKET_DIR } from "../../../pi-extensions/lib/socket-discovery.js";
|
|
87
90
|
import { probeSocketLiveness } from "../../../pi-extensions/lib/socket-probe.js";
|
|
88
91
|
const HOME = os.homedir();
|
|
89
92
|
// ============================================================================
|
|
@@ -431,11 +434,22 @@ server.tool("entwurf_v2", "CANONICAL DELIVERY SURFACE for garden ids: message, r
|
|
|
431
434
|
// Resolved ONCE so the dispatch-moment timestamp is fixed and the control RPC sender
|
|
432
435
|
// + the mailbox body sender share one envelope. No replyability gate (see above).
|
|
433
436
|
const sender = await buildSendSenderEnvelope({ requestMeta: extra._meta });
|
|
434
|
-
|
|
435
|
-
//
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
437
|
+
// #107: the operator's remote-pair opt-in is read per send. Absent = off and the local
|
|
438
|
+
// surface below runs exactly as before; a declared target goes over that one route.
|
|
439
|
+
const rendered = await runEntwurfV2WithRemoteRoute({ target, intent, message, mode, wants_reply }, {
|
|
440
|
+
senderProvider: () => sender,
|
|
441
|
+
route: readRemotePairOptIn(remotePairOptInPath()),
|
|
442
|
+
// The decider's canonical socket dir AND this bridge's ENTWURF_DIR: a trace in either is local.
|
|
443
|
+
localTrace: makeLocalTraceFact({
|
|
444
|
+
sessionsDir: defaultMetaSessionsDir(),
|
|
445
|
+
controlSocketDirs: [CONTROL_SOCKET_DIR, ENTWURF_DIR],
|
|
446
|
+
}),
|
|
447
|
+
transport: processTransport,
|
|
448
|
+
// No trust-preflight inputs are passed, and none exist to pass: the preflight on this
|
|
449
|
+
// path guarded the resume verdict and left with `owned-outcome`. `senderProvider` is
|
|
450
|
+
// the whole options surface now.
|
|
451
|
+
runLocal: (params, opts) => runAndRenderEntwurfV2FromSurface(params, opts),
|
|
452
|
+
});
|
|
439
453
|
return rendered.isError ? textErr(rendered.text) : textOk(rendered.text);
|
|
440
454
|
}
|
|
441
455
|
catch (err) {
|
|
@@ -60,13 +60,19 @@ export function formatMetaMailboxBody(sender, message, wantsReply) {
|
|
|
60
60
|
const replyable = sender.replyable === true;
|
|
61
61
|
const isMeta = sender.origin === "meta-session";
|
|
62
62
|
const kind = isMeta ? "meta-session, " : "";
|
|
63
|
+
// A remote sender is never replyable here: no reply route was probed. Its cwd is a path on the
|
|
64
|
+
// SENDING host, so it is neither abbreviated against this host's HOME nor shown as a local path.
|
|
65
|
+
const remote = !replyable ? sender.remote : undefined;
|
|
63
66
|
const sessionLine = replyable
|
|
64
67
|
? `${sender.sessionId} (${kind}replyable — reply via entwurf_v2 to this sessionId, intent=fire-and-forget)`
|
|
65
|
-
:
|
|
66
|
-
? `${sender.sessionId} (
|
|
67
|
-
:
|
|
68
|
+
: remote
|
|
69
|
+
? `${sender.sessionId} (remote via ${remote.via}; ${remote.replyRoute === "declared" ? "reply route declared, not probed" : "no reply route declared"})`
|
|
70
|
+
: isMeta
|
|
71
|
+
? `${sender.sessionId} (meta-session, non-replyable)`
|
|
72
|
+
: `${sender.sessionId} (external, non-replyable)`;
|
|
73
|
+
const where = remote ? `${sender.cwd} (sender-host path)` : abbreviateHome(sender.cwd);
|
|
68
74
|
return (`[entwurf received ⟵]\n` +
|
|
69
|
-
` from: ${sender.agentId} @ ${
|
|
75
|
+
` from: ${sender.agentId} @ ${where}\n` +
|
|
70
76
|
` session: ${sessionLine}\n` +
|
|
71
77
|
` at: ${formatKstTimestamp(sender.timestamp)}\n` +
|
|
72
78
|
` wants reply: ${wantsReply ? "yes" : "no"}\n` +
|