Skip to content
This repository was archived by the owner on Sep 7, 2026. It is now read-only.

Commit 5db74bf

Browse files
committed
fix(cli-agent-bridge): fail closed without reliable containment
1 parent 50664e4 commit 5db74bf

5 files changed

Lines changed: 514 additions & 153 deletions

File tree

plugins/Hylouis233/cli-agent-bridge/README.md

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,12 @@ repository lock; use separate clones when the comparison must run in parallel.
4848

4949
- Node.js 20 or newer to run the MCP server (the server has no npm dependencies).
5050
- git available on PATH; the target workspace must be a git repository.
51-
- Supported operating systems: Windows and Linux. The server is plain Node.js; on Windows,
52-
delegated workers run inside a kill-on-close Job Object. End-to-end verified on
53-
Windows (Claude Code 2.1.226, Kimi Code 0.30.0) and validated on Linux in a Node 22 container.
54-
macOS/BSD fail closed before Git or backend launch because this dependency-free server cannot
55-
prove containment of a child that clears its environment and escapes its original session.
51+
- Production delegation and workspace inspection are supported on Windows, where delegated
52+
workers and potentially extensible Git helpers run inside a kill-on-close Job Object.
53+
End-to-end verified with Claude Code 2.1.226 and Kimi Code 0.30.0. Linux, macOS, and BSD fail
54+
closed before backend configuration, workspace access, Git resolution, or executable probing:
55+
this dependency-free server cannot prove lifecycle containment after an arbitrary local worker
56+
asks a host service to launch a process outside its original process tree.
5657
- Each backend CLI must be installed, on PATH, and signed in with your own account before use:
5758

5859
| Backend | CLI | Status | Headless form used |
@@ -123,18 +124,18 @@ you already obtained a valid ID from that backend outside this Plugin.
123124
Git-ref compare-and-swap in a private bare repository at
124125
`<git-common-dir>/cli-agent-bridge-lock-store.git`. Keeping coordination refs out of the target
125126
repository prevents `git push --mirror` from publishing host/process/token metadata.
126-
On Linux, the private store publishes its persistent UUID through an exclusive ordinary-file
127-
compatibility anchor and the same-value UUID blob behind a create-only Git-ref CAS, without
128-
requiring hard-link support. The bridge keeps a common-directory handle open through release. Renaming the repository
127+
The private store publishes its persistent UUID through an exclusive ordinary-file compatibility
128+
anchor and the same-value UUID blob behind a create-only Git-ref CAS, without requiring hard-link
129+
support. The bridge keeps a common-directory handle open through release. Renaming the repository
129130
therefore cannot create a second lock domain or strand the original holder on its obsolete
130131
pathname, while deleting and recreating a repository cannot inherit the old lock identity. A stale
131132
idle lock is reclaimed only when its same-host owner is positively
132133
confirmed dead; owner records include the bridge process start identity so a reused PID cannot
133134
pin the queue. The host identity also includes the OS user, so another user cannot interpret a
134135
user-scoped quarantine marker as cleared. Malformed, foreign-user/host, starting, running, or
135136
uncertain records fail closed.
136-
A crashed bridge cannot reconstruct descendants that escaped into another POSIX session from the
137-
recorded worker PID alone, so inspect leftover processes before clearing its coordination ref.
137+
A crashed bridge can interrupt the durable state transition that follows worker cleanup, so inspect
138+
the recorded process identity before clearing its coordination ref.
138139
- Linked worktrees share refs and therefore intentionally share one repository lock. The
139140
`repositoryConcurrency` field remains as a fail-safe disclosure if an older bridge instance or
140141
an external writer updates bridge history during a snapshot, but current bridge instances do
@@ -151,18 +152,9 @@ you already obtained a valid ID from that backend outside this Plugin.
151152
For these reasons workspace_status is not marked
152153
read-only in its MCP annotations even though the snapshot itself does not edit worktree files.
153154
- Cancellation and timeout confirm that the delegated process tree has exited before releasing
154-
the workspace mutex. A lightweight ancestry monitor records descendants that create a new POSIX
155-
session/process group so cancellation still terminates them; tracked PIDs are matched against
156-
their recorded start identity (process start time on POSIX, creation time on Windows) so a
157-
reused PID is never signaled, and a POSIX process group is only signaled while its original
158-
leader identity still matches. On Linux, descendants also inherit a per-run environment marker;
159-
if the parent exits before ancestry polling, the close path uses a bounded observation grace and
160-
marker scans recover children that become visible just after the leader exits, using stable
161-
identities from `/proc`. If the kernel exposes tasks but not their `children` files, a verified
162-
startup capability check switches to a full PID/PPID snapshot while preserving the same marker
163-
and immutable-identity checks. Under extreme Linux process churn, if an identity-stable ancestry
164-
sample cannot be completed, the bridge conservatively quarantines the workspace for the same
165-
operator-verified manual recovery described below. Repository discovery and read-only snapshots
155+
the workspace mutex. On the supported Windows production path, a kill-on-close Job Object is the
156+
kernel lifecycle boundary; the bridge does not substitute PID ancestry polling or inherited
157+
environment markers on unsupported platforms. Repository discovery and read-only snapshots
166158
resolve Git before entering the workspace and explicitly disable repository hooks, fsmonitor,
167159
pagers, external diff drivers, text conversion, and detached automatic maintenance.
168160
Worktree status and unstaged diff commands are additionally process-contained because Git may
@@ -190,8 +182,6 @@ you already obtained a valid ID from that backend outside this Plugin.
190182
recovery (including repositories shared deliberately by multiple users). New markers are
191183
atomically claimed directories, so publication does not depend on hard-link support and the
192184
reported path is renamed the same way during recovery.
193-
On Linux, zombie-only tracked trees count as terminated; zombies cannot edit the workspace and
194-
may otherwise persist when container PID 1 does not reap them.
195185
- Cancelling a workspace_status request interrupts its queued lock wait or Git snapshot and returns
196186
a cancelled tool result instead of performing a stale snapshot later.
197187
- timeoutMs is an overall deadline that includes workspace lock acquisition, preflight Git checks,
@@ -232,8 +222,8 @@ They cover the full MCP flow plus in-process and cross-process canonical worktre
232222
owner compare-and-swap, live-owner non-steal, quarantined-lease recovery after an explicit
233223
operator approval rename, interruptible lease state updates, shared quarantine markers, queued and
234224
discovery-phase cancellation (including list_backends probes), overall deadlines, cancel/timeout
235-
process-tree termination, escaped POSIX descendants and zombie-only Linux groups, PID-reuse
236-
identity checks before signaling, unusual Git pathnames (including a trailing-space worktree
225+
Windows Job Object termination, internal fail-closed process-tree state-machine fixtures,
226+
PID-reuse identity checks before signaling, unusual Git pathnames (including a trailing-space worktree
237227
root), JSON-RPC id typing, unborn HEAD and non-HEAD ref changes, checkout-only HEAD moves,
238228
single-count attribution for commits on the checked-out branch, fork-point diff baselines for
239229
new branches, non-commit refs, fetched-history exclusion, repository-wide serialization and

0 commit comments

Comments
 (0)