The local-service proxy factory's operational identity rechecked the
runtime root with the exact dev/ino/uid/mode/nlink tuple, but a
directory's st_nlink is 2 plus its subdirectory count on ext4, tmpfs,
and APFS. Every prepare_session mkdirat(2)s a svc-* staging directory
under that root (and inode-safe teardown removes it), so the root's
link count necessarily changes and the factory itself - or any second
factory sharing the same runtime root - failed the next operational
check as false "identity drift". Proven on Ghost at base 4900bda4:
baseline LastTest.log line 666, root nlink 2 -> 3 verified by
instrumentation; the whole downstream local-service test flow was
blocked. The term could never distinguish sanctioned staging from
tampering: only subdirectory creation moves a directory's nlink, files
and symlinks do not, and writes to this owner-only root are trusted
operator authority per session-policy. The production runtime root is
also shared with the gloved instance lock and control socket, so entry
enumeration whitelisting would add no real enforcement claim.
Drop only the root nlink term from operational(): the root's
dev/ino/uid/mode stay exact (replacement and permission drift still
fail closed, covered by the existing chmod test), and the
descriptor-pinned endpoint parents and sockets retain the exact
dev/ino/uid/mode/nlink recheck in endpoint_current(), which is where
the recorded endpoint identity drift claim (architecture.md) lives.
Unknown drift remains fail-closed.
Add a deterministic regression: two factories sharing one runtime root,
and one factory preparing consecutive sessions, must stay operational
across a prepare_session, while chmod on the root still fails closed.
The test fails on pre-fix code (Ghost: operational() false at the first
post-prepare check).
While validating on Ghost (kernel 6.12), the test's downstream
guest-socket ABA section needed two determinism repairs of its own,
both previously unreachable there because the baseline always failed
earlier: resolve the session directory by its exact staged svc-* name
instead of readlink of the detached open_tree mount (6.12+ renders
detached mount roots as "/" in /proc/self/fd), and stage the ABA
replacement listener at a fresh path before swapping it in so the
replacement cannot recycle the just-freed socket inode (the documented
same-UID ABA caveat) and accidentally satisfy the identity check.