Repository navigation
Releases: kalidke/ship-of-tools
Release list
Ship of Tools v0.6.6
Ship of Tools 0.6.6 is the final release of the 0.6.6 line. It is the build tested in use as 0.6.6-rc9.15, with only its version changed.
Install or update with the one-line installer for your system, as for any release; a box on 0.6.6-rc9.15 updates in place.
Known defects, carried to 0.6.7:
- On Linux, an interrupt sent to the REPL can leave a signal residue behind.
- A mail wake can replace a line a person typed and had not yet sent.
Evidence: the release commit differs from v0.6.6-rc9.15 (a61dc5f) in version strings only, 14 files, 18 lines each way. v0.6.6-rc9.15 passed its hosted runs: Release 39511322222 at a61dc5f; Rust 39500322075 at e5b9fac, which differs from a61dc5f by the same version strings.
Ship of Tools v0.6.6-rc9.15
v0.6.6-rc9.15 — 2026-10-20
Fixes
- The daemon's interrupt signal stops an eval that never yields, and the shim drops a signal in its own waits (REPL-INTERRUPT)
- The interrupt signal goes only while an eval is in flight, with tests that an idle REPL and an ended eval are never signalled, and the Julia 1.12 limit stated (REPL-INTERRUPT)
- One interrupt request sends one ordinary signal, only a forced one the burst, only for the eval it names, and the shim drops a signal in its tail (REPL-INTERRUPT round 2)
- A forced interrupt replaces a plain one's escalation, a drained chunk survives a stray signal, and the records say a loop with a safepoint (REPL-INTERRUPT round 3)
- LANE-C #38 a pool request carrying a secret parameter is decided by it alone, the cookie only a request with none
- LANE-C #26 native_premises discards its role entry records instead of forgetting them, so TMPDIR is left empty
- LANE-C INBOX-LOCK-TMPDIR the two inbox-lock suites strip TMPDIR's trailing slash
- LANE-C SCRATCH-PROFILE-GUARD every Windows suite that touches sot state refuses to start outside a scratch profile
- LANE-C SCRATCH-PROFILE-GUARD the guard ends the process, since exit in a dot-sourced file leaves the suite running
- LANE-C #26 native_premises checks its role entry with assert_once, and Entry::discard is deleted
- LANE-C SCRATCH-PROFILE-GUARD each Windows suite runs in a home of its own that the guard makes and removes, and the daemon suite ends only its own run's daemons
- LANE-C SCRATCH-PROFILE-GUARD the real profile comes from the process token, the library switch counts only in its own scope, the child starts in the caller's location, the guard logs its home, and the daemon suite ends only daemons it recorded
- LANE-C SCRATCH-PROFILE-GUARD the child loses the caller's XDG and Claude config variables and gets a Julia depot in its home, the guard test no longer kills pids, and the header words are exact
- A create claims its slug and its rollback acts only on the row it inserted (A1: undo_insert, durable toml restore, unreadable toml refuses, Windows limit named)
- A failed log roll prints once and retries after another cap of growth; the roll copies the last cap bytes, reads the size from the held handle and runs only in-run (D11a)
- The transport test's comment names a run accepted after the hello, not one waiting for the REPL (S6)
- Fix1-B53 B1: each installer runs git through one function that carries the stall bound, so the worktree add's lazy fetch is bounded and nothing leaks to the processes the installer starts
- Fix1-B53 B2 and S1: one path-refusal predicate in the library, copied into install.sh's front gate and sot-apply, refuses % \ ' " $ backquote & | ; space tab and newline in the prefix or home before any write, and both renders call it
- Fix1-B53 S2: the library's refusal and every other line that names a path print with printf, so dash's echo cannot read a backslash in the path as an escape
- Fix1-B53 N3: sot-apply refuses an unsafe prefix or home before the flip, logs the reason and drops the pointer, so no window launch loops on try-restart
- Fix1-B53 notes N1 N2 N4 N5 N6: the log-count comments name their keep of 2 or more, 13b's fixture is a cap-only case again, the fe-paths test pins cygpath's own words, and the records describe the new checks and the two log names that are constants
- Fix1-B53 N2: 13b's fixture counts the newest out of the cap total, so it is a cap-only case again and removes the one oldest file
- The binding-site list names insert_replacing, where the registry's same-slug carry now lives
- Every mktemp names its template, so macOS keeps no temporary file outside the child's folder (MAC-UPDATER-TMP)
Design (ADRs)
- ADR 0030 amendment: every tag is cut from main (adr)
Documentation
- LANE-C #38 the site folder's page names the secret-parameter rule and its test file
- LANE-C a page's assets outside its site folder are a 403 in a repository and a 404 outside one, as measured
- LANE-C the ops.rs comment on assets outside a site folder says what was measured (Unix prefix) and what was only read
- Contributing says every tag is cut from main
- The CI tag gate reads main only (ci)
- The release.sh promise names main only (scripts)
- The distribution connection names main only (integration)
- The deploy comment names a push to main only, not a tag (make)
- Docs and comments: state the rules without attribution (RULING-SWEEP)
- README, quickstart, install page and the scripts pages agree on which Julia the daemon runs and what the install's line says (JULIA-CHANNEL)
- PROCESS-NAMES, the release skill and a spawn test state their rules without naming a source
- PROCESS-NAMES, comm comments and tests state their rules without naming a source
- PROCESS-NAMES, ADRs state their rules without naming a source
- PROCESS-NAMES, Rust comments state their rules without naming a source
- PROCESS-NAMES, Julia test comments state their rules without naming a source
- PROCESS-NAMES, rule comments drop the process role named as their source, and the dangling pointers, citations and PROTOCOL input-box wording are made whole
Tests
- An interrupt stops an eval that never yields, and a signal in the shim's waits is dropped (REPL-INTERRUPT)
- One interrupt request sends one ordinary signal, only a forced one the burst, an interrupt naming other evals sends none, and a stray signal in the shim's tail is dropped (REPL-INTERRUPT round 2)
- A forced interrupt a moment after a plain one forces, a drained chunk survives a stray signal, and the collection test fits a small runner (REPL-INTERRUPT round 3)
- The two-writer lock tests wait 60 s, past the suite's hang guard (HUB-FILES-LOCK)
- A signal during the pidfd wait is no answer (START-UNWIND-RED)
- The update test writes its program copy with no writable descriptor in the test process (UPDATE-ETXTBSY)
- The Windows junction helper in state_folder_tests runs mklink through test_wait::run_within, bounded, not Command::output (LINE-FIX-1)
- The live trash fixture's readiness waits on the leader's start alone, with 30 s bounds (LINE-FIX-1)
- LANE-C #38 pool auth table, one case per class, red where a stale ?secret= rides the new cookie
- LANE-C SCRATCH-PROFILE-GUARD the guard test runs its children from files, so stderr stays text
- LANE-C SCRATCH-PROFILE-GUARD the probe suite no longer dies on an empty -Name, which ended both concurrent probes at once
- LANE-C SCRATCH-PROFILE-GUARD every guarded suite and the guard test end with an explicit exit 0, so no step ends on a stale LASTEXITCODE
- The interrupt test's compiled function has a unique name and its follow-ups name nothing the interrupted eval defines (LINE-FIX-1)
- RELEASE-FROM-MAIN, release.sh's branch guard: a cut from main passes it, a cut from fixes/* or rc/* is refused by it (red on the base: both refused cuts reach the origin check)
- RELEASE-FROM-MAIN, rust.yml runs the branch-guard test in checks-linux beside release-before, and scripts/tests/CLAUDE.md lists it
- The Julia environment and floor cases move to installer-entry-julia.sh, sourced by installer-entry.sh, which the two merged installer lanes put past 800 lines
- The binding-site list names the stamp command that unpins the child (D4-SPLIT)
- A registry only the join clearance finds unreadable refuses a join from outside any row, red where the writer alone refused (IDENTITY-FC N4)
- The bind case restores its own copy of the registry, and the s1 case marks the held handle's mail so another case's leftover inbox cannot read as it (IDENTITY-FC)
- The REPL interrupt tests accept the daemon's own unanswered text as well as the shim's interrupted:true, still requiring the eval's InterruptException and kept+1 (MAC-INTERMITTENT)
- PROCESS-NAMES, test fixtures use neutral handles instead of a workstation name
- The settings.json multi-value testset closes before the dialog-hook testset, as the merge of the installer and D4 lanes left it open
- D8, a command's temporary file goes with the updater command and with the installer's interrupted Julia step
- Rc-gate names a shell suite's result by its checkout path, and refuses a result file emitted twice (GATE-BASENAME)
- Std::fs::copy joins the test-children lint, and each sanctioned data copy carries an allow with its reason (GATE-PARITY)
- Rc-gate runs rust.yml's three clippy lines and, when the tree differs, under-bash.sh under a cached bash 3.2.57, and gives every job a home of its own (GATE-PARITY)
- Rc-gate job commands write to the container's folder, not the logs folder (RC-GATE-LOGS)
- Installer-entry.sh's every-exit verdict case moves to installer-entry-exits.sh, so the suite fits 800 lines with batch 11's cases
- The append-open pin names daemon_log/mod.rs, where D11B moved the sotd.log open from main.rs
- The rc-gate parity suite passes a bash 3.2 folder under RUNNER_TEMP on a hosted runner (PARITY-HOSTED-DIR)
- The Windows apply and layout fixtures write the prepared.json record the update pointer verb reads, and a stage with none is refused (APPLY-PS1-ROLE)
- Pin sot_bounded's two copies byte-identical (SHELL-BOUND-ONE)
- Install.ps1 suite covers the daemon-children ask and keeps the fail-closed case with a REPL child (INSTALL-ONE-COMMAND)
- The descriptor-birth tests wait on the layer's named bound and prove the lock order without a clock (MAC-BIRTH-LOCK)
- The macOS arm of the birth-lock test releases only once its constructor is at the lock, so it fails when a constructor skips the lock (MAC-BIRTH-LOCK)
Other
- 0.6.6-rc9.15-dev opens the line after v0.6.6-rc9.14 [skip ci]
- LANE-A #49 a script under Stop calls every native command through Invoke-SotNative, so no caller's redirect ends it on stderr
- LANE-A #45 a value spliced into a sed or regex replacement lands literally (sot_sed_literal, a MatchEvaluator), and sot-apply.ps1 no longer quadruples backslashes
- LANE-A #43 a dev launch decides comm by the sam...
Ship of Tools v0.6.6-rc9.14
v0.6.6-rc9.14 — 2026-10-18
Fixes
- The status line counts a detached HEAD and does not take the index lock
- A file's preview.changed drops its cached hash and parse latch, and a rewritten annotation is read again, so STALE follows an edit and a reconcile without a window restart (CONCEPT-STALE-HASH)
- The drift cache is cleared on a workspace switch and a reconnect, a parse reply installs only while its path is latched, and the fire gate and install are pure functions the sequence test runs (CONCEPT-STALE-HASH round 2; the one call in on_preview_changed is covered only by the native window harness)
- A workspace switch, restore and reconnect also clear the parse failure records, so the retry comment is true (CONCEPT-STALE-HASH)
Documentation
- A root-relative page also ends when another window takes its port, and the site records name serve_request and an owner
- The records say the dev line's rules, a source build is a
+in the version, and CI gates the cut at HEAD - The process-group rule covers steps that run until stopped, and names its trade under KILL and its one Git Bash failure (TEST-WIN-ORPHANS)
- The Windows job's and the gate's known limits, and the rm-guard scans, are recorded where their rules live (TEST-WIN-ORPHANS)
- The connection map records the pluto_starting answer the window must recognize (B13 S3)
- ADR 0049 states the Windows boundary as a token of this user, impersonators with SYSTEM outside isolation, and puts the refusal clause with the Unix arms (B13 N5)
- A dev build goes onto Windows by sotd update stage, and prepare checks out its commit
- The update stage row says the command can downgrade, logs its steps and exits 2 on bad arguments
- Sot-setup says the status line is three lines, not two
- The list of a page's link refusals is complete, and the data-roots file is said to have no effect on Windows (B22 review S2)
- ADR 0024's cost sentence states every case the walk widened (B25-FIX S1)
- Update sot from the repo is one command and one verdict line, in the sot-install skill, INSTALL-AGENT.md, the README, AGENTS.md and the updating page, which now describes the version folders, rollback and self-update as they are (U1 ENTRY-UNIX)
- The sot-install skill, INSTALL-AGENT.md and the updating page leave target and direction to the person, never a version the agent picks, and say what a held or launcher-started daemon costs and what --archive runs (U1 ENTRY-UNIX FIX: findings 2 and 3, note 9)
- INSTALL-AGENT.md's read-before-you-run paragraph pins the tag with SOT_INSTALL_TAG, since a downloaded install.sh run without it fetches and runs the resolved tag's own installer instead (U1 ENTRY-UNIX: piping paragraph)
- The Windows entry install.ps1 in scripts/CLAUDE.md, the tests page, the ownership rows, the sot-install skill, INSTALL-AGENT.md (its Windows update section, 2b collapsed to the one command), install.md, updating.md and ADR 0030's amendment that the installer is the update and a named version pins (U2 ENTRY-WINDOWS)
- Updating.md, INSTALL-AGENT.md and the sot-install skill state the role order (the topology entry, then a role flag, then the record), that an install.json with no pin field is unpinned, and that a named run of the installed version pins in place (U2 ENTRY-WINDOWS)
- The skill, INSTALL-AGENT.md, install.md, updating.md, README, the scripts pages, ownership and integration say what the Windows entry fix does: no routine command names a version, long runs go to the background, the pin is in the verdict, the role order, the unknown daemon, the launcher log contract (U2 ENTRY-WINDOWS FIX 2, 9, 11)
- The Unix daemon-ask sentence is scoped to Windows, a FAIL's pin and armed-update text, a named run fetches no release asset, and the julia error kind are in updating.md, the scripts page and the sidecars page (U2 ENTRY-WINDOWS FIX2 R2 R3 and notes)
- ADR 0030 and scripts/CLAUDE.md say a pack install of the line is recorded with its + version and a source checkout is not (U2 FRESH-DEV)
- ADR 0030 and scripts/CLAUDE.md say the hub sync is separate from the record decision and the record script is the target checkout's own (U2 FRESH-DEV FIX)
Tests
- A page of sixty files loads through six browser connections past a gateway that refuses new logins, and fails until a page server keeps its connection open (PAGE-PROXY-PARTIAL)
- A window that redials keeps the pages it opened, and a forgotten window or a connection of no window ends them (SITE-SURVIVES-REDIAL)
- A window's redial connects before its old connection ends, and its pages still serve
- A kept connection to the video server checks its grant token on every request
- A close's wait line names what the leave waits for (CLOSE-PROMPT-VISIBLE)
- Comm-probe.sh finds a row that never joined, up waits for each join, and its typed line runs in cmd.exe (COMM-PROBE-WIN)
- Rm-guard runs end_group on the stub watcher's pipeline, a member that ignores TERM, and the leader-only stop that leaves the pipeline (TEST-WIN-ORPHANS)
- Every stub suite ends its watcher and nc as the process group each leads, so Git Bash, which has no pkill, leaves no pipeline member (TEST-WIN-ORPHANS)
- A Windows box runs a suite in a job object that ends every process it started, at its exit, its bound, or its ssh session's end (TEST-WIN-ORPHANS)
- End_group given a step that leads no process group says so, kills and reaps it, and returns 2 instead of waiting for its pipeline (TEST-WIN-ORPHANS)
- Comm-deps' tethered case checks and ends the loop's whole group with no pgrep, and temp-files uses lib-wait's group_gone (TEST-WIN-ORPHANS)
- Rm-guard rejects a hermetic suite that runs pkill or pgrep and a stub's pid taken outside set -m (TEST-WIN-ORPHANS)
- Rc-gate links julia into a folder of its own so no other binary shadows node, and a job's julia is proved to be the one the gate chose (RC-GATE-JULIA-PATH)
- In-job.ps1 finds a bare command on PATH before System32, quotes every word, says when it cannot watch an ssh session, and writes no progress to stderr; CI parses it (TEST-WIN-ORPHANS)
- The page-site pool tests take the pool one at a time, so a parallel test cannot take a port the pool test counts
- The macOS volume fixture turns indexing off and retries a busy detach for 30 s, logging the holders, so an outside process holding the image no longer ends the test
- The macOS teardown detaches the image's disk, not its mount path, and retries while hdiutil info still lists it
- The macOS teardown's holder listing and hdiutil info run under a bound whose expiry is logged and skipped, never a panic, and the refusal is printed first
- A holder listing that names the test's own pid fails the macOS teardown at once, so a leak by the test is never retried away
- The macOS fixture drops its unproven mdutil step and the always-true cfg on mod detach, and the records state the detach bound, its worst case and the own-pid rule
- A connection of this account whose process the daemon cannot read is admitted without it (WIN-SSH-PEER)
- The Windows peer child ends on a byte the parent writes, not on the close of a stream the parent's blocked runtime may not have dropped (WIN-SSH-PEER)
- The Windows peer child's read has a deadline of its own, so a refused stream fails the admission assertion in seconds instead of the isolation bound (WIN-SSH-PEER)
- The Windows admission test reads the peer child's process under a copy of its token with every privilege removed, as the daemon's filtered token has none, whatever its runner holds (B13 B1)
- The lifetime harness opens a notebook again when the daemon answers pluto_starting, within its 600 s (B13 N1)
- The ensure replaces a daemon of another version, sotd same-version answers, and comm follows the checkout's stamp (red at this parent)
- A preview reaches every window, and a browser open or a wglshow page opens in the one window in use
- A wheel step reports presence, red with the hook removed (B18 review N3)
- The stage fixture runs tar through sot_log::test_wait, as the disallowed-methods gate requires
- Each skill's canonical copy and its install payload stay byte-identical
- Each topology and create test owns its temp folder, and the guard removes it on unwind
- In-container.sh --tmpdir-empty fails a job that leaves files in its fresh TMPDIR
- The write-bound test removes both temp files it makes
- The write-bound and keep tests hold their temp files in a guard that removes them even when an assertion fails (B22 review S3)
- The page test binds no shared port and a failed fixture removes its folder (REPL-PAGE-ANNOUNCE)
- The page test's setup names why WGLMakie failed to build, the CI page doc is true, and three more tests let a TempDir remove their folder (REPL-PAGE-ANNOUNCE, B27-REVIEW)
- Fixtures copy the REPL shim and Pluto folders whole so the WGLMakie extension is tested, and one OwnedRoot end (SHIM-EXT-TESTED, B27-REVIEW NOTE 4)
- An OwnedRoot end whose removal fails leaves the retry to its drop, and one copy_release_folder in sot-log's test-support leaves out every Julia manifest (SHIM-EXT-TESTED, B29-REVIEW)
- The failed-removal test's releaser waits 2 s before it unlocks, and done.rs wraps its doc line at 120 (B29-REVIEW re-check notes R1-1, R1-2)
- A child process running serve writes nothing but envelopes to stdout when an eval calls display (REPL-DISPLAY-WIRE)
- Test-install-manifest.ps1 pins install-manifest.ps1's pointer rule, a changed install drops the armed pointer and a same-version rewrite keeps it (UPDATER-ARMS-INSTALLED)
- Test-sot-apply.ps1 section 9 writes its pointers as the Rust arm does, so the installed sotd can read them, and checks the sotd's own answer first (UPDATER-ARMS-INSTALLED)
- Test-install-manifest.ps1 reads no exit code, since the script ends without one and Select-Object -First 1 can end the version command early (UPDATER-ARMS-INSTALLED round 2)
- Test-install-manifest.ps1 names its exit-code check with a delimited variable (UPDATER...
Ship of Tools v0.6.6-rc9.13
This is v0.6.6-rc9.13, re-cut. Its first tag never published: the release notes, which list every change since
rc9.12, were longer than GitHub allows for a release, so the tag was removed and the release re-cut with the notes
capped. Every change since rc9.12: v0.6.6-rc9.12...v0.6.6-rc9.13
After you install
- Updating a window computer from rc9.12: use the two scripts, never Ctrl+Q or Task Manager. Once the hub runs
rc9.13, closing an rc9.12 window with Ctrl+Q can wait on the hub, which refuses its protocol, and its keep-sessions
question shows only on the bottom line. A window ended without Keep ends every session on its computer. Stage with
relaunch-sot.ps1 -NoBuild(in%LOCALAPPDATA%\sot\repo\current\scripts), wait forsot.exe --update-statusto
readarmed yes, then apply withrelaunch-sot.ps1 -Converge: the sessions keep running throughout. A session that
did end can be resumed: reopen its row in the same folder and type/resumein Claude Code. - Update every computer that talks to another. This release speaks wire protocol 3, and rc9.12 and older speak 2;
each refuses the other. Until a computer updates, its window shows the protocol-mismatch screen for an updated
daemon, and mail between it and an updated computer fails; its own rows keep running. - A home folder shared by several computers. Install once on the computer that should run the daemon: the installer
pins the daemon there, and the other computers stop starting one. Installing on a second computer that shares the
home moves the daemon to it, and the installer names the computer it moved from. On every other computer that shares
the home and ran rc9.12 or older, once:systemctl --user daemon-reload, thensystemctl --user stop sotd.service
(and eachsot-relay-tunnel@<host>.serviceit runs), thensystemctl --user reset-failed sotd.service.
Never runsystemctl --user disableon a shared home: it disables the unit for every computer that shares it. - A computer installed before rc9.13 and reinstalled for backend use only keeps starting a local daemon at login
unless its home is shared. If no other computer shares that home, runsystemctl --user disable sotd.service(the
installer prints this). - Old temporary folders. Daemons before rc9.13 left a folder for every Julia process they started in the temporary
folder (sot-julia-and six letters and digits). This release names its folders differently and cleans up after
itself, but never touches the old ones, since it cannot tell an old daemon's live folder from a dead one. Once no
older daemon runs on a computer (sot-fe versionshows none), list them:
find "${TMPDIR:-/tmp}" -maxdepth 1 -user "$(id -un)" -type d -regextype posix-extended -regex "${TMPDIR:-/tmp}/sot-julia-[A-Za-z0-9]{6}"
and remove them by adding-exec rm -rf {} +to the same command. - Linux: install Xvfb (
sudo apt install xvfb) so GLMakie works in the REPL.
What changes
- On Linux each workspace REPL and the Pluto server get a private X display (Xvfb, 3840x2160), so GLMakie loads and its
figures come back as files, as CairoMakie's do. Without Xvfb the REPL says so at its first run; CairoMakie and WGLMakie
still work. - That display is the one listener another account on the computer can reach. A 128-bit cookie, which only the REPL
holds, locks it. When the display ends, the REPL ends with it, and so does every program holding the display's
cookie; the next run starts a new REPL that says why (ADR 0049). - Ending a REPL (a restart,
--fresh, a crash, the row's end) also ends the programs it started that still use its
display, detached ones included. - A pane that is not on screen no longer redials after a failure; it redials when you next view it.
- For a row created through a symlink or another spelling, a path copy and the crop message to the agent give the
resolved path (for example/private/var/...on macOS). - Each daemon keeps its Julia children's temporary folders inside one folder of its own (
sotd-julia-<random>), held
by a lock for the daemon's life. A start removes only the folders whose lock is free, so a daemon running in another
pid namespace, or on another computer that shares the temporary folder, keeps its own. The release check's scratch
folder is held the same way. - The REPL's and Pluto's shutdowns answer every request accepted before the close.
Known limits
- Across computers, held folders need a file system whose locks every client sees (NFS with its lock service). An NFS
client cut off from its server past its lease (90 s by default) can lose its lock, and with it its live folder. - A temporary folder on a file system that refuses locks (NFSv3 without its lock service) stops every kernel, REPL and
Pluto start, and the release check fails with "making the release check's scratch folder". - A REPL run accepted after a reconnect's hello, while the REPL is still starting, is not named to that window.
- Quote a
comm-sendmessage in single quotes: inside double quotes the shell runs backquotes and$( )first.
Changes since rc9.12
v0.6.6-rc9.13 — 2026-10-15
Fixes
- Install_comm creates a missing comm folder 0700
- The comm scripts create the comm folder private and tighten one an older release left
- The comm folder's tightening warns from the end state and walks only an open folder
- The Stop hook and the auditor are private from their first line, and give the caller's mask back only where a user's writer starts
- The comm folder's tightening touches only the layout's own entries and refuses a folder that is not a comm folder
- The tightening never resolves a path through a layout folder, and its records say what is true
- The Stop hook runs the auditor through one audit() under the caller's mask
- PROTOCOL.md's layout block is the tightening's list, a user's own files beside it keep their mode, a relative comm folder is exported absolute
- The tightening's find lines keep the lock-manager and reclaim names apart, as test-hub-files's scan of the library requires
- The installer reads its list and scripts whole before it can refuse them, so a refusal leaves no file open
- Bound transition tests and repair provenance records (mac-id)
- Capture logs without timestamps (test-support)
- Honor Codex hook disablement before input (hooks)
- Silence failed agent-layer input opens (comm)
- Activate every workspace through one spawn recipe (repl)
- Make quit prompt respond by key identity (window)
- Bound forced leave delivery on the OS clock (lease)
- Bound runtime shutdown after the event loop (window)
- Guard lease fixture before binding (test)
- Derive default handles from shared safe pieces (codex)
- Publish each staged file by sibling rename (comm)
- Use declared host for registry and self slots (comm)
- Overlap the first supervisor and voyage ssh logins (topology)
- Drop a spare when its supervisor attempt is abandoned (capsule)
- Keep the lane endpoint route immutable (topology)
- Bound and report owned lane child teardown (topology)
- Synchronize ssh spawn admission with link close (topology)
- Bound heartbeat context resolution through its final reap (comm)
- Parse and declare folder trust through TOML (agents)
- Resolve trust scope and report skipped records (agents)
- Record trust in the effective child config (agents)
- Delegate folder trust declaration to its owner (install)
- Refuse a verbatim declaration prefix with a parent name (agents)
- Build the parent-name case as text in the trust declare matrix (agents)
- Resolve result targets from the producing host's rows (window)
- Acknowledge result badges after a matching presentation (window)
- Preserve hash characters inside quoted values (settings)
- Select accounts by name rather than list position (picker)
- Share the header grammar used by stale-write checks (annotation)
- Validate read metadata before choosing a fallback (scale)
- Share video suffix and MIME classification (video)
- Carry remote semantics for generated hub relays (transport)
- Poll writes alongside the held frame reader (transport)
- Report a refused identity claim accurately (lease)
- Log the result workspace and path (relay)
- Bound final process teardown independently (window)
- Stop and join the retiring supervisor before restart (repl)
- Finish every announced execute report (repl)
- Own the proxy grant for the supervisor lifetime (pluto)
- Keep the listener port for the REPL lifetime (wgl)
- Reject Windows app-execution aliases by tag (julia)
- Refuse a link to an app-execution alias instead of skipping it (julia)
- Complete pane timing after checkpoint presentation (frontend)
- End the pane timing fixture's daemon and children with their parent (frontend)
- Restore the pane timing child's stdin watch and prove it (frontend)
- Remove the killed child's fixture root and prove it (frontend)
- Count a missing pane timing root as removed and report the children first (frontend)
- Workspace.reauth resumes only a transcript started in the target row's root
- Workspace.reauth accepts only a session UUID as the resume id
- A reauth refusal names where the transcript started, and a relative start directory is refused
- Dir_identity opens only a directory, so a FIFO path fails at once
- A reauth moves the account only on the row it validated
- Keep native storage-exhaustion codes through volume preflight and Windows error context (log)
- Give storage exhaustion its own leg exit code and keep an owned leg's exit status (capsule)
- Hold a row while its storage is full and resume it when storage clears (capsule)
- Never wait for storage without the fence; refuse a new row on a full volume (capsule)
- A destroy or a window's close ends a row held for storage (capsule)
- A leg's diagnostics never panic on a full log volume (capsule)
- Recognize Windows quota exhaustion and only Error::Io as ...
Ship of Tools v0.6.6-rc9.12
v0.6.6-rc9.12 — 2026-10-03
Documentation
- The monitor drawer samples a daemon's own host on Linux only
- Comm skill troubleshooting — the dim line in an idle input box
- Drop the false dropped-id clause; a hub link is not on the roster
- The supervisor dial is in run_worker; test_env names the Windows variables
- Running.md states that every computer with a window runs its own backend
- ADR 0050, the window close ends this computer's sessions; amendment notes; known limits
- ADR 0050 states main's six rulings, and every citation names the right one
Tests
- A second agent inside a session has no comm identity (fails first)
- Comm suites fail closed on daemon discovery; the agent-layer tests grow
- The agent-layer gate's fix-round-2 cases, failing before the fixes
- A child may not spawn or despawn, hooks write nothing under the comm home, an unreadable Windows command line never dedupes
- Exact argument encoding, a refused comm-probe, comm-list on a fresh home
- Guard the rm sites the bridge tests added
- The comm_wake stub redraws its box clean, like a real one
- Tests, windows: read the kept-log line unwrapped
- The aim guard re-runs the aim rule just before its write
- A Windows test's capsules end with its Env, and no test launches a real agent
- The Windows capsule enumeration's stdin script ends with an empty line
- Sections 13 and 14 allow the folder before the kept log's leaf name
- The Windows capsule sweep matches a path in its short and long forms
Other
- A second agent inside a session has no comm identity
- The agent-layer gate reads full command lines and fails closed
- A refused child writes nothing; the agent-layer gate fails closed on a parse that did not finish
- A child neither spawns nor despawns, hooks write nothing under the comm home, an unreadable Windows command line never dedupes
- Backend tests: wall-clock asserts become order proofs (LF, RACE; HG, NEG, LOW, LAT kept)
- Backend tests: order proofs observe the waiter; the retry schedule is pinned; concurrent spawn asks what can run (review)
- Backend tests: spawn counts cannot race the supervisor's own respawns; review nits (round 2)
- Backend tests: the spawn-gap check states its limits; review wording (round 2 close)
- Log tests: fault_kill kills only after the capsule's first frame (order proof)
- Log tests: fault_kill waits for producer output; the seen prefix must survive (review round 2)
- Log tests: fault_kill's producer stops when its PTY dies or at 60 s; known limits (round 2 close)
- Lane/wallclock-fix (fb0c76f, the wall-clock test fixes) into fixes/0.6.6-comm
- The rc gate as concurrent jobs under one cap (cap 10, per-test split of the four serial binaries)
- Every job is accounted for; split binaries run once (review)
- The gate owns every process it starts; leftovers fail it (review round 2)
- Leftovers are seen on every path; the verdict cannot move (review round 3)
- Say that ALLDONE marks the end of the run, not a pass
- ALLDONE FAILED on any nonzero rc
- Name the socket_unix stdout interleave as a known limit
- Challenge_unix glues result lines too (known limit)
- Lane/rc-gate (c7cfeef, the rc-gate script) into fixes/0.6.6-comm
- One fsync before the pty write; forwarded rides the next commit (F3)
- Pin the stale-epoch refusal fsync in the input-WAL test (F3)
- Lane/f3-fsync (7d8dcdf, F3: the input-WAL fsync pin) into fixes/0.6.6-comm
- The agent footer is not watched and a row in its Stop hook is not typed into
- A stop mark dated ahead of now holds nothing; the landing claim is honest
- The wake reads the registry before the clock, and the mark's bounds are stated as they are
- Encode, never fold, an argument's newline, TAB and return; gate comm-probe; one gated ensure_home
- Gate every comm-probe verb; a TAB in a Windows command line is raw
- Lane/agent-layers (96b8281, the closeout: exact argument encoding, every comm-probe verb gated, one gated ensure_home) into fixes/0.6.6-comm
- Log tests: each protected sweep dir has its own refusal row (review round 2)
- Lane/orphans (32efd95, log tests: each protected sweep dir has its own refusal row) into fixes/0.6.6-comm
- F1 commits on producer quiet, not channel quiet (review S2)
- Lane/latency S2 (f63574a, capsule: F1 commits on producer quiet, not channel quiet) into fixes/0.6.6-comm
- Latency tests: k3b's ssh dials fail closed and check the daemon (review round 2)
- Lane/latency k3b (4be01dd, latency tests: k3b's ssh dials fail closed and check the daemon) into fixes/0.6.6-comm
- MIN_COMMIT_GAP counts from the last fsync flush_output! made, not the last output commit
- --help says what --start-maximized and --start-monitor do
- Resume the last mode at start unless --start-mode names one
- Ctrl+Arrow moves focus only among the panes the layout shows
- The agent pane's right border runs unbroken past the drawer line
- The preview clips at the last whole line, never mid-row
- Input typed into a disconnected pane is discarded and counted, never replayed
- A protocol refusal says in the agent pane which side is out of date
- Preview tables and overlays clip at their last whole row too
- Ctrl+Down enters the pane the layout's drawer shows, not always the REPL
- The discard count shows on its own row beside any pane reason
- An out-of-date daemon leads the agent pane's reason
- A harness run opens in the default mode, never the persisted one
- Merge lane/fe-defects (86fdabd) into fixes/0.6.6-comm
- Merge the comm hooks into every Claude account's settings.json
- The hook merge never creates a file under .claude-auth, keeps a user's command sharing a group, and leaves an unchanged file alone
- The settings merge stages at the file's own mode, compares dirs by real location, and reports a failed merge
- Merge lane/installer-hooks (308ab66) into fixes/0.6.6-comm
- The edit view scrolls to the end of the annotation
- Merge lane/fe-defects R1 (cbae583) into fixes/0.6.6-comm
- A linked account folder counts as inside .claude-auth, and the install names every settings file left without the hooks
- Merge lane/installer-hooks follow-up (82f37d1) into fixes/0.6.6-comm
- The preview scroll limit measures only the text on screen
- Merge lane/fe-defects R2 (b610dc5) into fixes/0.6.6-comm
- Only a Linux daemon samples its own host
- Merge lane/macos-sampler (7bc39f9) into fixes/0.6.6-comm
- Waiting clears a question; a textless blocked or waiting is refused
- Split Windows command lines as the Microsoft C runtime does
- Comm-worktree-new.sh refuses a second agent before any git write
- A macOS (name) record has arguments unknown, as on Windows
- A refused stamp writes nothing, and only rc 2 skips the idle hook's floor
- Comm-worktree-new.sh --help prints its whole header
- Note that a literal (...) command line reads as arguments unknown
- Guard the rm sites the stub-bin test added with ${VAR:?}
- Waiting clears a question silently, and the bare-stamp refusal reads one flag
- Merge lane/comm-066 (3b9127e) into fixes/0.6.6-comm
- A process that names a row must run inside that row's capsule
- The row id strips this host's own label first; the row rule's limits stated truly
- The Windows walk reads Cygwin's /proc to a native parent, and sotd walks on from there
- Merge lane/row-ancestry (cbc8ac1) into fixes/0.6.6-comm
- A directed relay send waits for its receipt even when the roster is empty
- A late filing makes the empty-roster FAILED not final; say so
- Merge lane/d1-relay (ef57d2b) into fixes/0.6.6-comm
- One counted-drop reader for commands read while not attached
- Every failed dial waits the doubling backoff, before the first attach too
- Link gate: one per-host gate for every frontend ssh start site
- Link gate: a worker pauses while its host's link is down and its pane is not viewed
- Tests that keys typed during a held re-attach handshake are never delivered
- Input typed before an attach is discarded and counted, never delivered
- The session pane counts keys discarded before it shows attached
- Put the consumed-bytes doc back on its function
- A reused warm client already attached clears the pane's discard count
- An input dropped whole is counted, never lost uncounted
- Frontend tests: the link-gate transport tests write session state under a temp dir
- Link gate: correct the docs that overstated pausing, make ssh_bridge::argv private, list known limits
- Link gate: zero the discard count after dispatch, isolate Windows test state, name sites in words
- Merge lane/link-gate (54e0b51) into fixes/0.6.6-comm
- A named session's input box counts as free for the wake
- Wake only on evidence the input reaches the main agent, never into a draft
- A row the wake keeps refusing is logged once per streak
- The last check reads the live screen, and Enter goes only into main's box
- The tests prove each guard by itself; Enter is its own step
- Merge lane/wake-border (01d580f) into fixes/0.6.6-comm
- The record says what is known: Enter not confirmed, the unguarded window, the hook marks
- Two doc clauses the round-5 change made false
- Merge lane/wake-border (9fc374c) into fixes/0.6.6-comm
- A wake that may have sent something says so
- Merge lane/wake-border (acf2334) into fixes/0.6.6-comm
- The window lease, its intents, bounds and exit codes
- One library renders the unit and the all-in-one wrapper
- The backend ensure waits for a successor and starts what this install owns
- Ensure the backend before every frontend spawn; re-exec after an update
- Re-render the unit and the wrapper this install owns
- Both rollbacks restore the unit and the wrapper
- The local-daemon ensure waits for a successor; stop waits for a shutdown
- Ensure before every respawn; the converge holds a lease
- A failed apply leaves the old record and files whole
- The ensure logs under the install and probes nc without a pipeline
- Pin a real foreign unit; small fixes
- Windows tests: section 9 checks the branch that reads the ensure; a throw...
Ship of Tools v0.6.6-rc9.9
v0.6.6-rc9.9 — 2026-10-01
Features
- Sotd stdio-bridge takes --host, never a caller's label (comm)
- The ssh: endpoint scheme, and the death of the port series (comm)
- The frontend's transport is an ssh child, as amended (fe)
- Stop opening tunnels, stop parsing what no longer exists (launcher)
- One gate every endpoint value leaves comm-lib.sh through (comm)
Fixes
- A carriage return from a Windows jq must not reach curl or gh (comm)
- Wire the carriage-return suites into CI, and name what the outbound strips defend (comm)
- The class timeout-on-a-shell-function, the daemon's missing ssh: arm, and a source-built box's lost relay endpoint (comm)
- One Dial enum replaces two mutually exclusive Options, docs-shots' dead --tcp arm, a private hostname (fe)
- The subshell warning stops naming a variable this stage deleted (docs-shots)
- A noisy but successful ssh: send stopped reporting FAILED (comm)
- One verdict, decided by the record and not by the transport (comm)
- A reply window that cannot open does not unsay a filed frame (comm)
Documentation
- Messaging on one page — ADR 0049, the protocol's delivery section, and the session's start page
- Carry the hub link, the free-prompt rule, and today's real strings into the one page
- End the self-contradiction, fix ADR 0047's record, and cut third copies
- The naming rules say where they are superseded, and the inbox is not a queue
- Comm comes back, not armed, and a skill does not say "this lane"
- The daemon already holds the handle — only the waking is outstanding
- A known defect is never parked in a later line
- The messaging design of record reaches this branch, honestly labelled
- What the review caught — the Windows fact, and a status that claimed too much
- The grep the status hands the reader has to name a real path
- A status line a script can check, and a real port in AGENTS.md
- The invariant check has to pass on the directory it documents
- The review's blocker — 0045 retires claims in three records I called current
- The review's tail — one overstatement, and a list that could drift unwatched
- Make the index a generated view, so it cannot rot unwatched
- The index check has to work in any locale
- The state pages 0034 owed, in a follow-up commit because the lane ran out
- The relay unit's stdio-bridge takes no label
- The project-local setup skill matches its shipped copy again
- Rewrap the registry-lock paragraph in comm.md
- The data-roots file and the .git rule say what the fix does
Tests
- Six more harnesses watched the real comm registry
- The rule in the state page, and all 36 combinations against it (comm)
- Every comm suite runs in its own HOME and refuses the live comm home
- The home guard also refuses the account's own comm home
- Every comm suite that runs a comm script is guarded, and the guard drops the daemon route
- The kept-dir cleanup waits for its helpers instead of spinning past them
- The guest's forward is tested through the binary, not by including its source
- The unlocked-reader case reaches the unlocked path by identity, not by hiding flock
- The deps suite's cleanup rm names its guarded roots
- A kept e2e dir is stopped by one abort file, and two claims the review found false are gone
- The e2e reads clock skew over one connection, bounded by its round trip
- The registry's writer and reader across two boxes on one home
- Three suites the registry lane broke pass again
- The home guard drops the host session's CLAUDE_CODE_SESSION_ID
- The e2e logs every strict send's wake latency
- Say what the e2e latency is measured from
- The status test checks its socket path before it starts a daemon
- A blocking lock wait's bound carries the box's load
- The e2e harness drops its per-send wake ping count (5a)
- Guard the rm -rf paths the rm-guard suite checks
Other
- A connection declares the handles it files for
- A box the hub has heard from is never silently absent
- Fix two cut-blocking review findings before rc9.8
- The hub lists sessions on every attached box (rc9.9)
- Six more harnesses watched the real comm registry (rc9.9)
- Messaging on one page — decision 0031 Part 1 lands as ADR 0049, the protocol's delivery section, and the session's start page (B7)
- Say only what ADR 0049 says, nothing to arm
- Delete the git bus and its callers
- Stop asserting as gone what the tree still runs
- Dead code, a stale comment and a plumbing verb, cleaned up
- Two more places that told a session to arm a Monitor
- One rule instead of two strings, and a real resume gap named
- No skill tells a session to arm anything, and the git bus goes (step 3 of decision 0031)
- A jq that writes CRLF must not fail a send or reset a cursor
- Sot_jq must not swallow the last record of a stream
- The probe resolves row ids the same way, so it gets the same fix
- No jq pipeline of ours ends in head, and three claims come down
- A jq that writes CRLF cannot fail a send, and the wrapper stops eating the last record (the Windows line-ending lane)
- Main's docs step and its release rules, resolved so each page stays true of its own branch
- The ADR status lines and the index reach the 0.6.6 line
- Run the verdict table, so the rule it proves cannot quietly stop being true
- Merge S1 of the isolation work: messaging keeps working with no shared plane
- Every inbox append is taken under the kernel's file lock
- The hub files for its own home, and a script appends only on a shared lock
- An NFS v4 lock counts as shared only with local_lock=none
- T11 shows how far the two hosts interleaved, not only that they ran
- A wire send files through the hub and reads one verdict
- Only the hub records the inbox lock, and every daemon checks it at each filing
- "filed" means kept, and no path appends without the lock
- A comm folder is the daemon's own only when the folder says so
- A cut-back length and an unknown lock each have one source
- An append and its cut-back use one descriptor
- An unknown lock binds the comm folder to the one machine that wrote it
- The daemon waits for the inbox lock in the queue, not by polling
- A cut line is never counted, and a reader steps back if one was
- The lock wait fits the lock manager, and T11 proves what it claims
- Readers can lock the inbox on NFS, and the cursor hashes the line it showed
- A delete rooted in a variable aborts when the variable is empty
- A daemon with no machine id says so, and the proof cases prove overlap
- An end-to-end run with the real readers, on the real mounts
- A lock error is never "busy", readers and the check share one line hash
- The end-to-end run fails on a lock fault a reader names
- Every lock fault reaches the session, and a marker turn does not end on unread mail
- A turn the end-of-turn check holds keeps its closing marker
- The round-8 review's fixes — a kept e2e dir stops its readers, and the feedback record is tighter
- The lock record's exclusive create decides from the record, not a link count
- Merge B1b L1: the lock record decides from the record; the forward test drives the binary
- Delete the per-session bridge — the hub files for every handle its folder lists
- Merge B1b L2: delete the per-session bridge
- A missing jq, flock or perl is named to the session, never passed silently
- Merge B1b L3: a missing jq, flock or perl is named to the session
- Merge B1b 8c: one abort file stops a kept e2e dir; two false claims gone
- The lock record's create returns the link's own answer, and one re-read decides every failure
- Merge B1b L1b: one re-read decides every failed link; the guest is one folder
- A test-only hook makes the failed-link re-read reachable, so a record found after a failed link is tested to decide
- Merge B1b L1c: a record that appears between the read and the link decides the start, tested
- A batch whose verdict cannot be read pings instead of passing silently
- B1b L2/L3 review fixes (items 2-6; item 1 left open)
- Merge B1b fix23: the L2/L3 review's fixes, items 2-6
- Retired bridge verb logs once and sleeps
- Merge B1b item 1: the retired bridge verb logs once and sleeps
- The fix range review's should-fixes
- The install prunes every comm script the release no longer ships
- The install record is published with a plain rename, like every other install file
- Merge B1b prune: the install removes every comm script the release no longer ships
- A retired bin script is checked for a running loop before the record prunes it
- The retired bridge verb sleeps on macOS too, and runs before anything is sourced
- The registry has one writer that never publishes an unreadable file, and one reader that says unreadable
- A zero-byte registry read is re-read, because another box can see a just-renamed registry empty
- A registry read that FAILED is retried by a new open, and only a missing file is absent
- The hooks read and write the registry only through the library, and ensure_home links a flushed skeleton
- The two locked registry writers read through read_registry_fresh
- Merge lane/b1b-l4: the registry's single writer and failed-read retry
- The L4 re-review's should-fixes: one registry read in shell and daemon
- The registry lock names its holder, and a dead one is reclaimed on its own machine
- The clear vouches for the holder only, and the registry lock's bound is time
- Merge lane/b1b-l5 (the registry lock record) onto lane/b1b after L4
- The L5 re-review's should-fixes on the merged lock
- The registry lock's chain walk always ends, and proof-less IDs are never mine
- The registry lock's own marker goes with its lock, and "by hand" is a verdict
- T17 removes the lock through ${P:?}, as the rm guard requires
- Registry lock FAILED text is true for a lock gone at the last step
- The registry lock's "was held" text no longer sends a person to the clear; two timing-free tests
- A registry lock whose record could not be read is released, never by hand
- The lock's FAILED sentences name the un...
Ship of Tools v0.6.6-rc9.11
v0.6.6-rc9.11 — 2026-10-01
Other
- One ssh in flight per host during an outage, and no silent hello hang
- The supervisor loop logs why it died
- Revert the Disconnected teardown; a per-host gate replaces it later
- Keep the original error when ssh's stderr is appended; trim two tests
- Keep the cause chain when ssh's stderr is appended
Ship of Tools v0.6.6-rc9.10
v0.6.6-rc9.10 — 2026-10-01
Tests
- Keystroke latency against a private daemon (ignored)
Other
- Flush the echo when the producer goes idle (F1)
- TCP_NODELAY on the lane TCP stream (F4)
- Frame latency 1 (F7)
- The liveness probe runs off the input path
- Latency tests: k3b never dials an unchecked socket (review S1)
- Comm wake: read the Windows prompt, and never type into a working row
- Comm wake: Windows takes both prompt glyphs inside the box
- Comm status: a declaration with text supersedes the old note
- Review notes — name the fail-open and menu cases, keep a bare declaration's own text
- A menu, a dialog input or an output line is never a free prompt
- Pin the free-prompt clauses, use the real working-row capture, tidy tests
- Merge lane/win-wake: the daemon wakes Windows rows, never types into a working row, a menu or a dialog input; a declaration's text supersedes the old note
- Log tests: no capsule process outlives its test
- Log tests: capsule guard bounded wait, tighter sweep anchor (review)
- Merge lane/orphans: no capsule process outlives its log test
Ship of Tools v0.6.6-rc9.8
v0.6.6-rc9.8 — 2026-09-29
Tests
- The bridge tether, measured in both directions
- Sweep both fakebin watchers, not just the one this cleanup was written for
- Isolate the wake suite per case, and reap the bridge fake
- The ping-reaper daemon watched the real comm registry
Other
- Wake a frontend-box session on a ping, not a harness Monitor
- One notice per cycle, not one per inbox
- Carry the admission rule with the source, delete the tag
- A silent daemon slows the wake, it never ends it
- A sender that is not a string must still wake
- The ping starts at the read cursor, not the file's end
- Reach the agent across the Windows process boundary
- One liveness question, because kill -0 lies on git-bash
- The start guard consults the process table, under a lock
- Bound the Windows walk, trust its parent links less, scan in one pass
- The guard refuses only a ping watcher, and WAKE means armed
- Three doors, one rule — a row has a ping watcher or says it has none
- _survived answers the narrow question, in its one place
- Say that a Monitor is not a wake path
- The Monitor is gone — ping wake replaces the harness Monitor (ADR 0047)
- Retry the stage-commit rename, sweep leftovers, surface a block
- A staged update survives an antivirus scan (defect 0c)
- The session-start banner ranked purple above green, contradicting ADR 0044
Operator note — a one-time transition gap, not a defect
After updating to this release, a session that was already running keeps its
old wake path until its next session start, compaction or resume. A session
that had let its harness Monitor lapse under pre-rc9.8 scripts therefore stays
deaf to new mail until one of those happens.
Nothing is lost while it is deaf: delivery is an append to the handle's inbox, so
mail is durable and merely unread, and the session reads it at its next turn end.
The rc9.8 ping watcher does not expire, so this affects only sessions that were
running across the update, once. No fix is planned and none is needed.
Ship of Tools v0.6.6-rc9.7
v0.6.6-rc9.7 — 2026-09-28
Other
- Exploratory footer-layout switch for the hull ledger captures
- Pane-frame bottom padding, border stroke at the bottom of its space
- Give the panes a row back by taking the band off the row grid
- Merge the approved footer band for rc9.7
- Fix three stale comments in the footer band
- The declaration lives on the box, so a spawn never stops at the dialog
- A key our own installer writes is not a typo
- The trusted prefix is asked for, never assumed
- The environment may supply the prefix, and one check covers both
- Restore the home-folder default the owner ruled for
- The home folder by default, --trusted-root to point elsewhere
- A relative prefix says which supplier was relative
- The home folder is the trust scope, and nobody else supplies it
- The declared table is the only answer to what is trusted
- The declared table is the only answer to what is trusted