Skip to content

feat(locks): key owner liveness on pid + process start identity (#413) - #435

Merged
justrach merged 1 commit into
mainfrom
feat/prime-413-locks
Aug 6, 2026
Merged

feat(locks): key owner liveness on pid + process start identity (#413)#435
justrach merged 1 commit into
mainfrom
feat/prime-413-locks

Conversation

@justrach

@justrach justrach commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Implements #413 — the lease primitive the detachment arc (#431) needs, adopted from the prime-agent analysis.

What's in it

  • proc_identity.zig (460 lines): a three-valued Probegone, unknown, or an identity — where unknown (permission denied, odd errno) is never evidence of death. Identity sources: Linux /proc/<pid>/stat field 22 parsed from the last ) (comm may contain spaces and parens — fixture-tested with graff (test) x, where a naive split returns the wrong field), macOS proc_pidinfo (chosen over the issue's ps -o lstart=: no fork in a lock path, microsecond resolution so two graffs started in the same second can't share an identity; struct layout pinned by comptime asserts), Windows GetProcessTimes. Other platforms degrade to today's pid-only liveness.
  • The whole rule is one function: gone → reclaimable, unknown → held, identity → held iff the record's matches (or the record predates identities).
  • worktree_lease.zig gains the producer it was missing plus a live_unverified verdict; session_lock.zig's Two graff instances on one terminal corrupt each other's session and input (SIGTTOU suspend already fixed in #271) #289 degraded path (no working flock) now brackets writes with an owner-record claim instead of racing unguarded.
  • Backward compatible by construction: start_id == 0 = record from an older binary → exact pid-only contract; an in-flight lock is never bricked. The reusable owner-file protocol is shaped for the oauth-arc credential-store lock to adopt as-is.

Evidence

  • Tests 994 → 1005 (+11 exactly, stash/rebuild proof; reachability wired + verified).
  • Real two-process E2E, 17/17 on this machine: SIGKILL'd holder reclaimed via gone; a forged record naming a live sleep pid with a wrong start-id reclaimed while the innocent process is untouched; same pid with its real identity BLOCKED; legacy and bare-pidfile records honored both ways.
  • Cross-target zig test compiles clean for x86_64-linux-gnu / aarch64-linux-musl / x86_64-windows-gnu; CI's ubuntu leg exercises the real /proc path.
  • All 9 golden eval files PASS; fmt + line-guard clean; changelog entry under v0.0.241 (unreleased).

Part of the prime-agent adoption batch; unblocks #431 milestone 1.

A bare pid is not an owner. Pids are a small recycled namespace, so a
crashed holder's number is handed to something unrelated and a lock keyed
on the pid alone reads as held forever - or, keyed on a timeout instead,
gets stolen from a holder that is very much alive.

proc_identity.zig reads the START identity of a pid: /proc/<pid>/stat
field 22 on Linux (counted from the LAST ')', since comm may contain both
spaces and parentheses), proc_pidinfo(PROC_PIDTBSDINFO) on macOS - a
libSystem call rather than the `ps -o lstart=` subprocess the issue
suggested, so there is no fork in a lock path, no locale-dependent date
parsing and microsecond rather than one-second resolution -
GetProcessTimes on Windows, and pid-only liveness anywhere else.

Liveness is now "the pid is alive AND it is still the same process", and
only a provable mismatch makes a lock reclaimable. Two rules keep the
upgrade safe: a record with no start identity (an older graff's, or an
identity-less platform's) keeps the pre-#413 pid-only contract, so an
in-flight lock is never bricked; and a probe that FAILS is `.unknown`,
which means held - wrongly reclaiming a live lock corrupts, wrongly
honouring a dead one only waits.

Both lock modules take it up. worktree_lease.Owner.start_ns becomes
start_id and gains the producer it never had (selfOwner/probeOwners),
with a new live_unverified verdict for a pid we cannot identify. The
#289 degraded session write - a filesystem whose advisory locks do not
work - stops racing unguarded and brackets itself with an owner record,
via the reusable claimOwnerFile/releaseOwnerFile the credential store and
any future daemon lease can share.

Co-Authored-By: Codegraff <blackfloofie@codegraff.com>
@justrach
justrach merged commit f331b51 into main Aug 6, 2026
6 checks passed
justrach added a commit that referenced this pull request Aug 6, 2026
integration: the prime-adoption batch + engine slice 1c (merges #433 #434 #435 #436 #437 #438)
@justrach
justrach deleted the feat/prime-413-locks branch August 6, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant