feat(ghost,ci): ghostd exit-is-a-feature + ADR-0023 Part-1 integration gate - #224
Merged
Conversation
…DR-0023 follow-up) Restarting the TARGET under a LIVING sender is the one rebirth direction the qsh-exit leg never exercises — and the only one where Part 1's dead-target unlink is load-bearing in the live routing path (the living shell's stale lower-slot cap would win untargeted first-match and route to the dead pid; only unlink + the Pass-2 re-mint produce an answer). - GHOST_EXIT op: ghostd terminates on request (mirrors qsh's exit-is-a- feature); any ghostd IPC cap holder may ask, no reply, abuse bounded by the max_restarts budget. - qsh `ghost exit` sub-command (arg_of dispatch, usage on unknown subs). - ci-smoke: session sends `ghost exit` + a fourth `ghost` to the living reborn shell; new gate asserts a 'qsh: ghost R=' in the post-'GHOSTD: FIELD REBORN' slice (plus explicit GHOSTD: exiting and FIELD REBORN presence asserts). Inherently non-vacuous: a dropped kill never prints the banner and the slice is empty. - ADR-0023 residual-risks updated: the deferred integration leg is now shipped. Verified locally (WSL): full make ci-smoke PASSES; the reborn ghostd landed on a RECYCLED pid (5, was 17) — the exact scenario the unlink protects — and the living shell still answered. Co-Authored-By: Claude Fable 5 <[email protected]>
Co-Authored-By: Claude Fable 5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The follow-up increment ADR-0023 named in its residual risks: the qsh-exit leg proves Part 2 but never exercises the one direction where Part 1's dead-target unlink is load-bearing in the live routing path — the TARGET restarting under a LIVING sender.
Changes
GHOST_EXIT(op 6): ghostd terminates on request, mirroring qsh's exit-is-a-feature. Any ghostd IPC-cap holder may ask (the same trust domain that can already flood every other op); no reply; abuse is bounded by ghostd'smax_restartsbudget.ghost exitsub-command (standardarg_ofdispatch; usage line on unknown subs).ghost exitto the living reborn shell, then a fourthghost. New gate asserts aqsh: ghost R=in the post-GHOSTD: FIELD REBORNslice, plus explicitGHOSTD: exiting/FIELD REBORNpresence asserts. Inherently non-vacuous: a dropped kill never prints the banner and the slice is empty. Session timeout 26s → 34s.Why the gate proves what it claims
The living shell holds a qsh→ghostd cap across ghostd's death. Without Part 1, that stale cap sits at a lower first-fit slot than the Pass-2 re-mint and wins untargeted
send_msgfirst-match, routing to the dead pid — no answer. Only unlink + re-mint together produce the post-rebirthR=.Verification (local WSL)
make ci-smokePASSES; the reborn ghostd landed on a recycled pid (5, was 17) — the exact scenario the unlink protects — and the living shell still answered.ghost exitdelivered, ghostd reborn — and the Part-1 gate fails withqsh: ghost send denied (EPERM). Part 1 is load-bearing.🤖 Generated with Claude Code