Files
pad/internal
xarmian 0a32eff274 fix(scripts): install-refresh removes pad.pid only when it is provably not a live sibling's (BUG-4068) (#2311)
* fix(scripts): install-refresh removes pad.pid only when it is provably not a live sibling's (BUG-4068)

The refresh removed <data dir>/pad.pid unconditionally after the install,
whether or not it stopped anything and whoever the file named. A second
server sharing the data dir lost its record, so `pad server stop` and
`pad server status` could no longer find it.

Now the file is removed when it names the pid this refresh stopped, or a pid
that is provably dead (kill -0 fails and ps finds no such process, so EPERM
is not read as dead). It is kept when it names any live process, when its
liveness is unknown, and when it does not parse as a pid record (JSON or the
legacy bare integer). A kept stale file is the safe failure: server stop
refuses a record it cannot prove (BUG-2969). The BUG-4064 tests, which
clear a dead-pid file with nothing stopped, stand unchanged.

Claude-Session: https://claude.ai/code/session_01Hk4vxsaKDm3E1LvsoHtHeV

* test(buildtools): pin install-refresh's pad.pid removal rule (BUG-4068)

Claude-Session: https://claude.ai/code/session_01Hk4vxsaKDm3E1LvsoHtHeV

* fix(scripts,buildtools): pad.pid is read no more loosely than pad writes it, and only ESRCH is dead (BUG-4068 r1)

r1 items, as ruled:
1, 3: pid_file_pid accepts only MarshalIndent's record (one "pid", at the
   two-space top-level indent) or a bare integer with surrounding whitespace,
   at most 10 digits. Duplicate or nested pid keys, compact JSON, "1 2" and
   huge values no longer parse, so the file is kept.
2: pid_liveness reads the builtin kill's own message under LC_ALL=C. Only
   "No such process" is dead; EPERM and anything else are unknown. ps is not
   asked, so a missing, busybox or hidepid-restricted ps cannot call a live
   sibling dead.
4: STOPPED_PID is recorded only when kill -TERM is delivered.
5: the decision is pid_file_removable, tested on its own, so the stopped-pid
   leg (a zombie or a quick reuse) has a row of its own.
6: the dead-pid row uses a reaped pid, not 999999.

Also: the harness guard's testTempScope cuts the test name to 64 bytes, as
Go's makeTempDir does. A top-level test name of 64 bytes or more was refused
by its own temp dir; this unit's long test names found it.

Claude-Session: https://claude.ai/code/session_01Hk4vxsaKDm3E1LvsoHtHeV

* fix(scripts): pad.pid's reader checks the whole record's line shape and caps the pid (BUG-4068 r2)

r2 items, as ruled:
1: pid_file_pid now checks the record's line SHAPE end to end, not only the
   pid line: first non-blank line exactly "{", last exactly "}", every line
   between a two-space-indent key line, one of them the pid. A "\r" is
   dropped only at a line end. This is still a shape check, not full JSON
   validation. The r1 commit's "accepts only MarshalIndent's record" said
   more than r1 did; this commit is what makes the shape claim true.
2: a pid above 4194304 (Linux's largest pid_max) does not parse, so it can
   never wrap as a pid_t.
3: pid_liveness states that ESRCH is per pid namespace.
5: the stopped-pid test comment is corrected: the e2e pre-server is a zombie
   until cleanup reaps it, so the e2e stopped-server rows reach that leg too.

Claude-Session: https://claude.ai/code/session_01Hk4vxsaKDm3E1LvsoHtHeV

* docs(scripts,buildtools): pid_file_pid's comments state the inner-line check exactly (BUG-4068 r3)

Comment-only. pid_file_pid checks that each line between the braces STARTS
like a two-space-indent quoted key; the rest of such a line is unchecked.
The comments said "every line between a two-space-indent key line", and
"read no more loosely than the SHAPE", which claimed more than the pattern
checks (r3, ruled a decline: the code stays as it is after the close).

Claude-Session: https://claude.ai/code/session_01Hk4vxsaKDm3E1LvsoHtHeV
2026-10-25 06:26:02 -04:00
..