Skip to content

feat(supervisor): re-adopt orphaned daemons by default after a crash - #633

Merged
jdx merged 7 commits into
mainfrom
claude/orphan-adoption
Jul 24, 2026
Merged

feat(supervisor): re-adopt orphaned daemons by default after a crash#633
jdx merged 7 commits into
mainfrom
claude/orphan-adoption

Conversation

@jdx

@jdx jdx commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Context

Implements the re-adoption half of #631 (rebased onto main now that #632 is merged): the reporter asked that a restarted supervisor either re-adopt orphaned daemons or stop them before considering them startable. #632 made the kill path safe; this PR makes re-adoption the default, built on its fail-closed identity machinery.

Behavior

On supervisor startup, for each state daemon whose recorded PID is alive and whose identity (PID + kernel start time) is verified:

  • orphan_policy = "adopt" (new default): the process keeps running and supervision resumes — state, resolved ports, and proxy routing stay intact. Since the process is no longer a child of the supervisor, child.wait() monitoring is impossible; a poll monitor (2s) takes over, anchored to the verified start time so a later PID recycle is never mistaken for the daemon. It mirrors the regular monitor's exit path: stop transitions to stopped and fires on_stop/on_exit (even when stop() finalizes the record before the poll monitor wakes); unexpected death marks errored with unknown exit code (exit codes of non-children are unobservable), feeding the existing retry machinery — a retried daemon respawns as a normal child again. Legacy title-matched records get their start_time persisted at adoption.
  • orphan_policy = "kill": pre-adoption behavior — the pinned, fail-closed termination from fix(supervisor): verify process identity before killing orphaned daemons #632.
  • Unverifiable identity retains running state untouched (fail closed, both policies); dead or recycled PIDs reset state. cleanup_orphans = false still skips orphan handling entirely (legacy escape hatch; docs updated to point at orphan_policy).

Interval-watcher reconciliation

A new monitored registry (daemon → PID under a live monitor task) lets the 10s interval watcher catch state-running daemons that nothing is watching, applying the same fail-closed checks and orphan policy as the startup scan: dead or recycled PIDs are marked errored(-1) (previously a stale running record could persist forever when startup cleanup was skipped), live verified ones are adopted or terminated per policy. Registration happens synchronously before monitor spawn, with restart-overlap-safe RAII removal, so supervised daemons never appear unmonitored. The poll monitor re-verifies record ownership before mutating state, so a stale monitor can never clobber a successor process's state.

Known limitation (follow-up planned)

Log capture cannot be restored for an adopted process — its stdout/stderr pipes died with the old supervisor. Output capture resumes on the daemon's next restart. A follow-up will explore re-attaching via /proc/<pid>/fd on Linux (reopening the surviving pipe inode recovers capture and up to 64KB of gap output), and possibly FIFO-based capture for a cross-platform fix; connected-socket approaches don't work here since a broken stream fd can't be re-pointed at a new listener.

Tests

  • crashed supervisor re-adopts running daemon by default — same PID survives crash+restart, still running, start refuses a duplicate, stop terminates the adopted process and completes the transition
  • adopted daemon death is detected and marked errored — external kill of the adopted process is caught by the poll monitor
  • stopping an adopted daemon fires on_stop and on_exit hooks — regression for the stop-hooks race found in review
  • the kill test is pinned to PITCHFORK_ORPHAN_POLICY=kill; the recycled-PID bystander test is policy-independent and unchanged

Review feedback from Bugbot (stop hooks, kill policy on the interval path) and Greptile (successor-state clobbering, P1) is addressed in the current revision.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Supervisors can now reconcile orphaned daemon processes after restarting.
    • By default, verified orphaned daemons are re-adopted and continue running under supervision.
    • Added an option to terminate orphaned daemons instead.
    • Re-adopted daemons are monitored for termination and process identity changes.
  • Bug Fixes

    • Improved handling when daemon monitoring is unexpectedly lost.
    • Unverifiable or mismatched processes now fail safely without being terminated.
  • Documentation

    • Expanded configuration guidance for orphan verification, adoption, termination, and fail-closed behavior.

Note

High Risk
Changes core supervisor lifecycle around process identity, concurrent monitors, and startup/interval reconciliation; mistakes could kill wrong processes, duplicate daemons, or corrupt state/hooks.

Overview
After an unclean supervisor exit, verified live daemon processes are reconciled instead of always being killed. New supervisor.orphan_policy defaults to adopt: keep the process, preserve state/ports/proxy routing, and resume supervision via a 2s poll monitor (no restored stdio; unknown exit codes → errored and retries). kill preserves the prior terminate-on-restart behavior.

A monitored registry with tokenized MonitoredGuard registration (before Running state is written on normal starts) distinguishes supervised daemons from orphans. The interval watcher runs the same fail-closed identity checks and policy for unmonitored running records (dead/recycled → errored(-1)).

Docs/schema add orphan_policy; cleanup_orphans is described as reconciliation. Bats cover default adoption, adopted death/hooks, and pin the old kill test with PITCHFORK_ORPHAN_POLICY=kill.

Reviewed by Cursor Bugbot for commit 4fa3618. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Supervisor orphan handling now verifies process identity and applies an adopt or kill policy. Adopted daemons receive poll-based monitoring, state updates, and lifecycle hooks. Monitor registration and reconciliation ordering prevent unmonitored running daemons from being missed.

Changes

Orphan reconciliation

Layer / File(s) Summary
Orphan policy contract
settings.toml, docs/public/schema.json
Documents verified orphan reconciliation and adds supervisor.orphan_policy with adopt and kill values.
Monitor ownership tracking
src/supervisor/mod.rs, src/supervisor/lifecycle.rs
Tracks active daemon monitors and registers monitor ownership before spawning monitoring tasks.
Reconciliation and adoption flow
src/supervisor/adopt.rs, src/supervisor/mod.rs
Reconciles unmonitored daemons, validates PID and start time, adopts or terminates verified processes, and marks unverifiable deaths as errored. Adopted processes are polled and lifecycle state and hooks are updated on exit.
Adoption behavior validation
test/supervisor.bats
Tests default adoption, adopted-process failure detection, lifecycle hooks, and explicit kill-policy behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Supervisor
  participant OrphanedDaemon
  participant StateStore
  participant LifecycleHooks
  Supervisor->>StateStore: Find running daemon without monitor
  Supervisor->>OrphanedDaemon: Verify PID and start time
  Supervisor->>OrphanedDaemon: Adopt and poll liveness
  OrphanedDaemon-->>Supervisor: Terminate or fail identity check
  Supervisor->>StateStore: Record stopped or errored state
  Supervisor->>LifecycleHooks: Run applicable lifecycle hooks
Loading

Possibly related PRs

  • jdx/pitchfork#632: Shares verified PID and kernel start-time checks for orphan cleanup.
  • jdx/pitchfork#620: Overlaps with monitor-task lifecycle and guard registration.
  • jdx/pitchfork#635: Intersects with stale monitor callbacks and daemon state handling.

Poem

A bunny found a daemon astray,
And checked its start time before the day.
“Adopt,” said the burrow, “or safely depart,”
While monitors watched with a careful heart.
Hooks chimed softly when processes ceased—
Orphaned no longer, the supervisor feasted!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: orphaned daemons are re-adopted by default after a supervisor crash.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread src/supervisor/adopt.rs Outdated
Comment thread src/supervisor/adopt.rs
@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Re-adopts verified orphaned daemons by default after supervisor crashes.

  • Adds configurable adopt and kill orphan policies for startup and interval reconciliation.
  • Tracks monitor generations to prevent stale monitors and reconciliation passes from mutating successor state.
  • Adds polling-based lifecycle handling for adopted processes, including terminal state and hook processing.
  • Updates generated settings schema, documentation, and supervisor integration tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failures remain in the fixes associated with the previous ownership and successor-state threads.

Important Files Changed

Filename Overview
src/supervisor/adopt.rs Implements orphan reconciliation, monitor-generation ownership, adopted-process polling, and guarded terminal-state handling; the displayed ownership fixes appear complete.
src/supervisor/lifecycle.rs Registers each child monitor before exposing its running state, closing the reconciliation window discussed in the previous threads.
src/supervisor/mod.rs Integrates monitor tracking, periodic reconciliation, startup adoption, and orphan-policy selection into the supervisor.
settings.toml Defines and documents the new adopt-by-default orphan policy and updated cleanup semantics.
docs/public/schema.json Publishes the generated schema entry for the new orphan policy.
test/supervisor.bats Covers explicit kill behavior, default re-adoption, adopted-process death detection, and stop-hook execution.

Reviews (8): Last reviewed commit: "fix(supervisor): register monitors befor..." | Re-trigger Greptile

Comment thread src/supervisor/adopt.rs Outdated
Base automatically changed from claude/pitchfork-discussion-631-d2fa3d to main July 24, 2026 05:45
When the supervisor dies uncleanly, its daemons survive re-parented to
init. Previously the restarted supervisor killed them (or, before the
identity fix, could leave them running unsupervised). Now, daemons whose
recorded identity (pid + kernel start time) matches a live process are
re-adopted by default: their state, resolved ports, and proxy routing
stay intact and supervision resumes.

An adopted process is no longer a child of the supervisor, so wait()
monitoring is impossible; a poll monitor takes over, anchored to the
verified start time so a recycled PID is never mistaken for the daemon.
It mirrors the regular monitor's exit path (stop -> stopped with
on_stop/on_exit, unexpected death -> errored with unknown exit code,
feeding the existing retry machinery — a retried daemon respawns as a
normal child). Its stdout/stderr capture cannot be restored; log capture
resumes on the daemon's next restart.

- new setting supervisor.orphan_policy: "adopt" (default) or "kill"
  (pre-adoption behavior); cleanup_orphans=false still skips orphan
  handling entirely
- adoption requires the same fail-closed identity verification as
  termination: unverifiable identity retains running state untouched
- legacy title-matched records get their start_time persisted at
  adoption so future scans have a durable identity
- new monitored-daemon registry so the interval watcher can reconcile
  state-running daemons that lost their monitor, applying the same
  fail-closed checks and orphan policy as the startup scan: dead or
  recycled pids are marked errored(-1), live verified ones are adopted
  or terminated per policy
- the poll monitor re-verifies record ownership before mutating state so
  a stale monitor can never clobber a successor process's state, and it
  fires on_stop/on_exit even when stop() finalizes the record first
- registry entries are added synchronously before monitor spawn with
  restart-overlap-safe RAII removal, so supervised daemons never appear
  unmonitored
- bats coverage: adoption across crash/restart (same pid, no duplicate,
  stop works), adopted-death detection, stop hooks on adopted daemons,
  and the kill policy pinned via PITCHFORK_ORPHAN_POLICY=kill

Closes the re-adoption request from #631.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jdx
jdx force-pushed the claude/orphan-adoption branch from ef6d050 to f93d8a1 Compare July 24, 2026 16:28
Comment thread src/supervisor/adopt.rs
Comment thread src/supervisor/adopt.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
docs/public/schema.json (1)

1084-1090: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Constrain orphan_policy to supported values.

The runtime only accepts adopt and kill; schema validation currently accepts any string and tooling can validate a config that silently falls back to adopt.

Proposed schema constraint
 "orphan_policy": {
   "description": "What to do with live orphaned daemons on supervisor startup: adopt or kill",
   "type": [
     "string",
     "null"
-  ]
+  ],
+  "enum": ["adopt", "kill", null]
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/public/schema.json` around lines 1084 - 1090, Constrain the
orphan_policy property in the schema to the supported values “adopt” and “kill”
by adding an enum constraint while preserving its nullable behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/supervisor/mod.rs`:
- Around line 1349-1356: Update the supervisor decision flow around
process_identity_matches() so adopt and kill actions require a persisted
daemon.start_time that matches the current process start time; title-only legacy
records must retain their running state and receive neither action. Apply the
same start-time requirement in the reconciliation logic in adopt.rs, using its
existing identity and state-handling symbols.

---

Nitpick comments:
In `@docs/public/schema.json`:
- Around line 1084-1090: Constrain the orphan_policy property in the schema to
the supported values “adopt” and “kill” by adding an enum constraint while
preserving its nullable behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 21b355e5-ee10-4d97-9537-96b72eb2d5f2

📥 Commits

Reviewing files that changed from the base of the PR and between fa85337 and f93d8a1.

📒 Files selected for processing (6)
  • docs/public/schema.json
  • settings.toml
  • src/supervisor/adopt.rs
  • src/supervisor/lifecycle.rs
  • src/supervisor/mod.rs
  • test/supervisor.bats

Comment thread src/supervisor/mod.rs
Review follow-ups on the re-adoption feature:

- preserve active_port when persisting identity for legacy title-matched
  records at adoption; upsert_daemon intentionally never inherits it, so
  the previous upsert wiped a live daemon's port and broke proxy routing
  until restart
- gate all exit-path state writes strictly on still owning the record's
  PID; a successor that reached Stopping/Stopped before the recheck was
  previously mutated (pid cleared, active_port dropped) and could get
  duplicate stop hooks
- snapshot stop-in-flight status at the moment death is observed (the
  poll-monitor analog of the child monitor's pre-drain snapshot) so stop
  intent is tied to the observation rather than later state reads

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread src/supervisor/adopt.rs
…ted exits

A successor's complete start+stop between two poll ticks leaves the
record PID-less and stopped, which the previous ownership check
misattributed to the adopted process — firing its stop hooks a second
time. The monitored registry already acts as a generation marker:
run_once overwrites the daemon's entry synchronously before any
successor spawns, and each monitor's guard removes only its own value.
The poll monitor now stands down whenever the entry no longer names its
PID, covering successors that already finished.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread src/supervisor/adopt.rs
… check

Re-validate registry and PID ownership inside the same state-lock
critical section that clears the active port and writes the terminal
status, so a successor starting between the earlier ownership snapshot
and the mutation can never have its record overwritten — its upsert
serializes after ours and the recheck stands down.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread src/supervisor/adopt.rs
Comment thread src/supervisor/adopt.rs Outdated
adopt_daemon registered its poll monitor after an await (the legacy
identity upsert), so two overlapping reconciliation passes could both
pass the advisory is_monitored check and spawn duplicate monitors for
the same process. Registration is now a synchronous check-and-insert
(try_register) performed before any await: adoption never displaces a
live monitor, while run_once keeps its unconditional overwrite so a
spawned successor still supersedes a stale adopted monitor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread src/supervisor/mod.rs
);

let policy = orphan_policy();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Startup dead PID skips retries

Medium Severity

After a supervisor crash, startup orphan cleanup clears a still-running record whose PID is already dead to stopped, while the new interval reconciliation marks the same situation errored(-1). Because cleanup runs first on every restart, an adopted daemon that exits before the poll monitor persists state never becomes errored, so configured retries and fail hooks may not run.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 98df44c. Configure here.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The divergence is real but deliberate, and I'd keep it in this PR:

  • Startup's dead-PID → stopped is merged fix(supervisor): verify process identity before killing orphaned daemons #632 behavior (and predates it) — changing it here would silently alter semantics jdx already shipped, and the merged test suite pins it (orphaned daemons are cleaned up on supervisor restart asserts stopped).
  • The two paths answer different questions. At startup, a dead PID means "this daemon did not survive the supervisor outage" — historically treated as a clean slate. At runtime, the reconciler finding a dead unmonitored PID means "a supervised daemon died and nobody recorded it" — errored(-1) is what feeds retries there.
  • The scenario where this bites (adopted daemon dies, then the supervisor crashes again within the poll monitor's 2s window, so the death is only ever seen by the next startup scan) is a narrow double-crash race, and its outcome — stopped, no retry — matches what every pre-adoption version did for all crash-window deaths.

That said, I think there's a fair argument that startup should also mark previously-running dead daemons errored so retries fire after a supervisor crash — that would be a behavior change best made deliberately (and tested) in a follow-up rather than folded into this one. Happy to file it.

This comment was generated by Claude Code.

Comment thread src/supervisor/adopt.rs
Two successor-safety refinements to orphan adoption:

- registry entries now carry a unique registration token in addition to
  the PID. Guards unregister only their own token and monitors detect
  supersession by token, so a successor that recycled the same numeric
  PID can neither be unregistered by a stale guard nor be mistaken for
  the stale monitor's own process
- the reconciler's terminal-state writes (unmonitored death, recycled
  PID, kill-policy success) re-validate that the record still names the
  snapshot PID inside the same state-lock critical section that performs
  the write, matching the poll monitor's atomic finalization; a
  concurrently installed successor is never overwritten

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0f4bf18. Configure here.

Comment thread src/supervisor/lifecycle.rs Outdated
Comment thread src/supervisor/adopt.rs
run_once persisted the Running state (with PID) before registering the
monitor, leaving a cross-thread window in which the orphan reconciler
saw a freshly started daemon as an unmonitored orphan — spuriously
adopting it, or terminating it under the kill policy. Registration now
happens before the upsert, so any visible running record always has a
live monitor entry; the RAII guard still unregisters on early-error
paths.

finalize_if_pid additionally re-checks the registry inside its
state-lock critical section: with registration-before-visibility
guaranteed, a successor that recycled the snapshot's numeric PID is
detected by its monitor entry and never overwritten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jdx
jdx merged commit a06def0 into main Jul 24, 2026
13 checks passed
@jdx
jdx deleted the claude/orphan-adoption branch July 24, 2026 18:16
gaojunran pushed a commit to gaojunran/pitchfork that referenced this pull request Jul 25, 2026
The supervisor held the read end of every daemon's output pipe, which put
it in the data path. Killing it left the pipe with no reader, so the
daemon's next write took SIGPIPE and the daemon usually died with it —
undermining the re-adoption added in jdx#633, since a daemon that logs to
stdout rarely survived long enough to be adopted. Anything written but
not yet read was lost as well.

Capture now happens in a sibling process: `pitchfork log-sink`, this
binary re-executed, holding the read end and writing to the log store.
A supervisor crash is invisible to logging — the daemon keeps writing,
the sink keeps recording, and no line is dropped. This is the arrangement
runit uses, where runsv starts a service alongside its own log service
and stays out of the stream.

The supervisor retains a spare read end, which does two things: a sink
that dies cannot leave the pipe readerless and kill the daemon, and its
replacement can be handed the same pipe. It deliberately keeps no write
end, which would stop the sink from ever seeing end of file. A sink that
exits while its daemon is still monitored is replaced, mirroring runsv
restarting a log service; one that exits cleanly has reached end of file
and is not.

Failed starts report what the daemon printed by querying the log store,
which the in-process path guaranteed by flushing synchronously before
signalling. That ordering is restored by waiting for the sink's final
write, bounded so a daemon whose descendants hold the pipe open cannot
stall the caller.

Daemons using ready_output, an on_output hook, or pty = true keep the
in-process path, because the supervisor itself has to read the stream to
serve them, and so remain vulnerable to a supervisor crash. Teaching the
sink to evaluate those and report matches back is left for a follow-up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jdx added a commit that referenced this pull request Jul 25, 2026
## 🤖 New release

* `pitchfork-cli`: 2.17.0 -> 2.18.0

<details><summary><i><b>Changelog</b></i></summary><p>

<blockquote>

## [2.18.0](v2.17.0...v2.18.0)
- 2026-07-25

### Added

- *(supervisor)* re-adopt orphaned daemons by default after a crash
([#633](#633))
- *(project)* add project enter/leave/list for IDE session management
([#619](#619))
- *(config)* add top-level env with tera template injection
([#618](#618))
- *(daemons)* add --global flag to daemons add
([#621](#621))
- full Windows support with CI
([#602](#602))

### Fixed

- *(logs)* tolerate concurrent opens of the log store
([#660](#660))
- *(supervisor)* make child-monitor exit finalization atomic
([#659](#659))
- *(supervisor)* make unobserved daemon deaths retryable after a crash
([#657](#657))
- *(deps)* update rust crate clx to v3
([#656](#656))
- *(supervisor)* verify process identity before killing orphaned daemons
([#632](#632))
- *(web)* resolve spa assets from app root on nested routes
([#603](#603))
- *(tui)* hoist shared namespace into the daemons panel title
([#622](#622))

### Other

- *(procs)* use div_ceil for the process-exit poll count
([#658](#658))
- *(deps)* update rust crate tokio to v1.53.0
([#653](#653))
- *(deps)* update rust crate uuid to v1.24.0
([#651](#651))
- *(deps)* update rust crate http-body-util to v0.1.4
([#643](#643))
- *(deps)* update rust crate rustls to v0.23.42
([#646](#646))
- *(deps)* update rust crate syn to v2.0.119
([#647](#647))
- *(deps)* update rust crate tokio to v1.52.4
([#648](#648))
- *(deps)* update rust crate mdns-sd to v0.20.2
([#644](#644))
- *(deps)* update rust crate regex to v1.13.1
([#645](#645))
- *(deps)* update rust crate globset to v0.4.19
([#642](#642))
- *(deps)* update rust crate clx to v2.1.1
([#641](#641))
- *(deps)* update rust crate clap to v4.6.2
([#640](#640))
- *(deps)* update rust crate xx to v2.6.1
([#611](#611))
- *(deps)* update rust crate ratatui to v0.30.2
([#609](#609))
- *(deps)* update rust crate rcgen to v0.14.8
([#610](#610))
- raise MSRV to Rust 1.88
([#624](#624))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Version and documentation-only release PR with no runtime code changes
in the diff.
> 
> **Overview**
> **Release v2.18.0** — bumps `pitchfork-cli` from **2.17.0** to
**2.18.0** across `Cargo.toml`, `Cargo.lock`, `pitchfork.usage.kdl`, and
generated CLI docs (`docs/cli/commands.json`, `docs/cli/index.md`).
> 
> **CHANGELOG** gains a dated **[2.18.0]** section (2026-07-25)
documenting what ships in this tag; the **[Unreleased]** section stays
empty for the next cycle.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
74b9e9c. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
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