feat(host): add /restart slash command and IHostRestarter #37
No reviewers
Labels
No labels
bug
commercial
documentation
duplicate
enhancement
feature
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
jasoncouture/llama-shears!37
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/self-restart"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Adds a self-restart capability to the host, surfaced as the
/restartslash command (channel input only — per the no-UI-buttons-for-control policy, there is no UI button).Closes the Self-restart control item under TASKS.md → "Web UI" (the spec is "control surface", not literally a button).
What's in here
src/LlamaShears.Hosting/IHostRestarter— host-wide self-restart trigger. Single method,void RequestRestart().HostRestarter— default impl. Behavior:Interlocked.Exchange) — repeated calls past the first are no-ops.DOTNET_RUNNING_IN_CONTAINER=true.IHostApplicationLifetime.ApplicationStoppedfor the post-shutdown step.IHostApplicationLifetime.StopApplication().Environment.Exit(1)— supervisor (Dockerrestart: unless-stopped, k8s) brings us back. Non-zero exit is the only signal we owe.Environment.ProcessPathwithEnvironment.GetCommandLineArgs()[1..]verbatim;Environment.Exit(0)after spawn. The spawned child is independent of the parent.ProcessPathavailable, orProcess.Startthrows → log +Environment.Exit(1).Wiring
HostingServiceCollectionExtensions.AddHostRestarter(). Called fromWebApplicationBuilderExtensions.AddApi, so any host that goes throughAddApigets it for free.Slash command
ChatSession.TryParseCommandrecognises/restart;ExecuteCommandAsynccalls_restarter.RequestRestart().ChatSession's constructor now takes anIHostRestarter(DI handles it).ChatCommandenum picks up aRestartvalue.Why hook
ApplicationStoppedinstead of running re-exec eagerlyHosted services need to drain (release ports, flush state) before we either exit or spawn the child.
ApplicationStoppedfires after the host'sStopAsynccompletes, so by the timeFinalizeruns the listening sockets are already free for the spawned child to bind.Tests
2 new TUnit cases (
HostRestarterTests):RequestRestartCallsStopApplicationOnce— first call invokesIHostApplicationLifetime.StopApplication.RequestRestartIsIdempotent— three consecutive calls produce oneStopApplicationinvocation.The actual
Environment.Exit/Process.Startpaths can't be unit-tested without forking the test host; they will be exercised by hand the first time/restartlands in production.Test plan
dotnet test --solution LlamaShears.slnx— 384 passing (was 382).dotnet-test-on-source-changepre-commit check passed./restartfrom chat, observe new pid; in-container — same, observe Docker restarts the container.Stacking note
Targets
feat/cron-tool-stub(PR #36). Will retargetmainautomatically as the chain merges. The slash command dispatch is left in the ChatSession-direct-call form on purpose; PR 8 introduces the registry and migrates/clear,/archive,/compact,/restartto it.🤖 Generated with Claude Code
All committers have signed the CLA.
Pull request overview
Adds a host self-restart capability (
IHostRestarter+ defaultHostRestarter) and exposes it through a new/restartchat slash command in the Web UI, wired via DI fromAddApi.Changes:
IHostRestarterandHostRestarterto request a graceful shutdown followed by either container-exit or local re-exec.IHostRestarterinto the default service setup (AddHostRestarter()called fromWebApplicationBuilderExtensions.AddApi)./restartparsing/execution inChatSession, plus unit tests for idempotentStopApplication()triggering.Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
RequestRestart()callsStopApplication()once and is idempotent.AddHostRestarter().AddApi()./restartcommand parsing and dispatch toIHostRestarter.LlamaShears.Hosting.💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
@ -0,0 +1,122 @@using System.Diagnostics;Finalizeis a confusing method name in C# because it collides withobject.Finalize/finalizer terminology. Consider renaming it to something likeFinalizeRestart/CompleteRestartto avoid ambiguity and potential analyzer confusion.Process.Start(psi)returnsProcess?. If it returnsnullhere, the code still logs pid -1 and then exits with code 0, which can bring the host down without successfully spawning the replacement process. Treat anullreturn as a failure (log + exit non-zero) before exiting the current process.@ -0,0 +19,4 @@lifetime.Received(1).StopApplication();await Task.CompletedTask;}These tests are marked
asyncbut don't await any real async work (they end withawait Task.CompletedTask). Consider making them non-async (or returningTaskwithoutasync) to keep the intent clear and avoid unnecessary state machine generation.