on master help sign in
tools/ship: find the ship by its binary; read empty .txt files

processes() took any process whose arguments named the pier for the king, so tools/ship --pier /abs/pier status found itself, and stop could SIGTERM itself. Only urbit binaries count now. The manifest checked ?@ before the %txt mark, so an empty .txt file, whose wain ~ is an atom, came back as a cord and verify() said it differed after every commit. development.md blamed the ship for this; the caveats are gone, and the ship self-test now syncs an empty .txt. Review fix-first item 7 (A65, A66). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U4FzmSdubYxmZGDp2VJRAQ

commit941c486e3f603fabcf78ac97e27519d84c8e86a9 authorClaude <noreply@anthropic.com> dateMon 2 Nov 2026, 14:03 (9 hours ago) parent68d9b76
4 files changed+18−10
docs/development.md+1−3 tools/ship+16−6 tools/testdata/ship-golden/empty/about.txt+1−1 tools/testdata/ship-golden/empty/blank.txt
docs/development.md+1−3
@@ -207,12 +207,10 @@ A case is a folder `tests/golden/<name>/` holding:
207207- optionally, `path.txt`, the logical `/ui/.../*.jibb` path to pass to the compiler; the source remains `source.jibb`.
208208- optionally, an `expect-types:` line in `about.txt` opts the case into `+golden-types`; `types.txt` holds its output plus one newline and `--update` creates it. A `types.txt` also opts in when the line is absent. This interface is provisional while the checker is under construction.
209209- optionally, `imports.txt` selects Hoon `/sur` molds, one `module mold` pair per line. Each module's source goes in `sur/<module>.txt` and is compiled against zuse by both runners. `marks.txt` selects marks, one name per line, with source in `mar/<name>.txt`; both kinds may be supplied together. The reader results go to the partial checker, and a type output is required. Both runners reconstruct fixture text from `%txt` lines and identify the fixture if its compilation crashes; a crash fails the case without discarding its successful parse output. These fixtures currently handle only sources without Ford imports.
210−- optionally, `modules.txt` lists logical `/ui/.../*.jibb` paths, one per line. The corresponding sources live below the case's `modules/ui/` directory. Both runners parse each source at its logical path and compare the combined `module-trees.txt`; `--update` writes that output. A case with type output also supplies these sources and the vendored prelude to the partial module checker. The file opts in even when it lists no sources; use a comment such as `# no module fixtures` for an empty bundle, since zero-byte `%txt` fixtures do not sync reliably on the ship. Blank lines and comment lines are ignored. Cases without `modules.txt` keep their existing no-prelude type interface.
210+- optionally, `modules.txt` lists logical `/ui/.../*.jibb` paths, one per line. The corresponding sources live below the case's `modules/ui/` directory. Both runners parse each source at its logical path and compare the combined `module-trees.txt`; `--update` writes that output. A case with type output also supplies these sources and the vendored prelude to the partial module checker. The file opts in even when it lists no sources; use a comment such as `# no module fixtures` for an empty bundle. Blank lines and comment lines are ignored. Cases without `modules.txt` keep their existing no-prelude type interface.
211211
212212The runners prepare the trusted vendored public and internal prelude sources once for their generated test core and share the partial interface. Preparation reserves all names present in the public source and supplies the molds and values the fragment can infer; an unsupported declaration remains reserved but unavailable. The source and checker are incomplete, so the output still says the module has not been checked and does not claim complete shadowing coverage. `+golden-types-with-prelude-and-internal` accepts both sources directly; callers using `+golden-types-with-prepared-prelude` pass the unchanged result of `+prepare-prelude-with-internal`. The older source-only entry points remain available for preludes without imports. Missing or malformed sources fail closed.
213213
214−A zero-byte mark fixture currently fails ship synchronization validation after commit. Use a newline-only fixture to test an empty Hoon source on the ship.
215−
216214Names say what a case is for:
217215
218216| Name | For |
tools/ship+16−6
@@ -283,17 +283,25 @@ class Ship:
283283 # -- processes
284284
285285 def processes(self):
286− """(king pid, serf pid) of the urbit running this pier, or None."""
286+ """(king pid, serf pid) of the urbit running this pier, or None.
287+
288+ Only urbit binaries count: other commands whose arguments name
289+ the pier, such as tools/ship --pier /abs/pier itself, click or
290+ tail, are not the ship.
291+ """
287292 king = serf = None
288293 for pid in os.listdir('/proc'):
289294 if not pid.isdigit():
290295 continue
291296 try:
297+ exe = os.path.basename(os.readlink('/proc/%s/exe' % pid))
292298 with open('/proc/%s/cmdline' % pid, 'rb') as f:
293299 args = [a.decode('utf-8', 'replace')
294300 for a in f.read().split(b'\0') if a]
295301 except OSError:
296302 continue
303+ if not (exe == '.run' or exe.startswith('urbit')):
304+ continue
297305 if len(args) < 2 or self.pier not in [
298306 os.path.realpath(a) if a.startswith('/') else a
299307 for a in args[1:]]:
@@ -668,8 +676,9 @@ MERGE = r"""
668676# how many files the desk has; for each file sent with its content,
669677# whether the desk's mark, converting that content from %mime, gives
670678# the stored value; and for the files @@FROM@@ to @@FROM@@ + @@COUNT@@,
671−# the path with [%cord hash] for a cord, [%wain hash] for a %txt file's
672−# lines, each with a newline after it, or [%other 0]. A hash is the
679+# the path with [%wain hash] for a %txt file's lines, each with a
680+# newline after it (an empty file's wain is ~, an atom, so this comes
681+# first), [%cord hash] for another cord, or [%other 0]. A hash is the
673682# first 8 bytes of the sha-256. now is fixed across pages.
674683MANIFEST = r"""
675684=/ m (strand ,vase)
@@ -690,10 +699,11 @@ MANIFEST = r"""
690699 |= pax=path
691700 =/ have .^(* %cx (weld pre pax))
692701 :- pax
702+ ?: =(%txt (rear pax))
703+ :- %wain
704+ (end [3 8] (shax (rap 3 (turn ;;(wain have) |=(l=@t (cat 3 l '\0a'))))))
693705 ?@ have [%cord (end [3 8] (shax have))]
694− ?. =(%txt (rear pax)) [%other 0]
695− :- %wain
696− (end [3 8] (shax (rap 3 (turn ;;(wain have) |=(l=@t (cat 3 l '\0a'))))))
706+ [%other 0]
697707=/ jet=? =(1 (lent (to-wain:format 'a\0a')))
698708(pure:m !>([ud:.^(cass:clay %cw pre) now jet (lent all) conv page]))
699709"""
tools/testdata/ship-golden/empty/about.txt+1−1
@@ -1,3 +1,3 @@
11From: written for tools/ship test.
2−Tests: an empty source.jibb, which Clay stores as the cord 0; the expected files hold the output and one newline.
2+Tests: an empty source.jibb, which Clay stores as the cord 0, and an empty blank.txt, which the %txt mark stores as the empty wain ~, also an atom; the expected files hold the output and one newline.
33Expected: OK
tools/testdata/ship-golden/empty/blank.txtnew file

No changes to the content.