AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A pure-Go (no cgo) restart / reconcile state machine. Feed it a stream of
down / up / unhealthy / healthy signals for named units, and it decides
when to respawn after a death — honouring a grace period (anti-flap),
max_restarts within a sliding window (anti-thrash), and optional exponential
backoff — then effects the respawn through a caller-supplied Actions
interface.
Extracted from a microVM agent and made fully dependency-free: the
RespawnPolicy is a local value type (no external schema), health is a local
SignalKind (no probe/health-checker import), and side effects go through an
interface, so the whole machine is unit-testable with a fixed clock and a fake.
A pure decision core.
Decideis an allocation-light pure function:(policy, history, attempt, signal, now) → Plan. TheReconcileris the concurrent shell around it — one goroutine per watched unit, lazily started, draining a buffered signal channel — but every scheduling decision is a value you can assert against a fixed clock.
down/unhealthy signal parks in grace_wait for
grace_period_ms first, so a transient flap that recovers never triggers a
restart.max_restarts restarts are allowed per rolling
window_ms; exceeding it moves the unit to cooldown until the window rolls.constant or exponential (doubling initial_delay_ms, capped
at 5 minutes) before each respawn.Stop then Start, not a bare Start.Reconciler fans out across many units by
name; distinct units never serialise on each other.CGO-free, dependency-free, 100% test coverage (including every error
branch), race-tested, gofmt + go vet clean, and green across the six 64-bit
Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x).
go get github.com/go-proc/respawn
package main
import (
"context"
"time"
"github.com/go-proc/respawn"
)
// myActions effects the respawn. A real one wraps your process/VM driver;
// tests supply a fake to assert state-machine behaviour.
type myActions struct{ /* ... */ }
func (myActions) StartVM(ctx context.Context, name string) error { /* ... */ return nil }
func (myActions) StopVM(ctx context.Context, name string) error { /* ... */ return nil }
func main() {
r := respawn.New(myActions{}, nil)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
policy := &respawn.RespawnPolicy{
Enabled: true,
GracePeriodMs: 2000, // debounce flaps
MaxRestarts: 5, // within...
WindowMs: 60000, // ...a 60s window
Backoff: "exponential", // 1s, 2s, 4s, ... capped 5m
InitialDelayMs: 1000,
}
r.Watch(ctx, "web-1", policy)
// Feed signals from your event source:
r.Send(respawn.Signal{VMName: "web-1", Kind: respawn.SignalDown, When: time.Now()})
// On shutdown: cancel ctx, then drain.
cancel()
r.Wait()
}
Or drive the pure core directly, no goroutines:
plan := respawn.Decide(policy, history, attempt, respawn.Signal{Kind: respawn.SignalDown}, time.Now())
// plan.State / plan.Action / plan.DelayFor / plan.Reason
type RespawnPolicy struct {
Enabled bool
GracePeriodMs int64
MaxRestarts int32
WindowMs int64
Backoff string // "constant" | "exponential"
InitialDelayMs int64
}
type SignalKind int // SignalDown | SignalUp | SignalUnhealthy | SignalHealthy
type Signal struct { VMName string; Kind SignalKind; When time.Time }
// Actions is the side-effect surface the reconciler drives.
type VMActions interface {
StartVM(ctx context.Context, name string) error
StopVM(ctx context.Context, name string) error
}
// Reconciler — the concurrent shell.
func New(actions VMActions, log *slog.Logger) *Reconciler
func (r *Reconciler) Watch(ctx context.Context, name string, policy *RespawnPolicy)
func (r *Reconciler) Unwatch(name string)
func (r *Reconciler) Send(sig Signal)
func (r *Reconciler) Wait()
// Pure decision core.
type Plan struct { State State; Action Action; DelayFor time.Duration; Reason string }
func Decide(policy *RespawnPolicy, h *History, attempt int, sig Signal, now time.Time) Plan
func DecideAfterGrace(policy *RespawnPolicy, h *History, attempt int, kind SignalKind, now time.Time) Plan
Naming. The
Actionsmethods andSignal.VMNamekeep theVMnames of the origin codebase; the package itself is unit-agnostic — a "VM" here is any named thing you supervise.
The pure Decide core is asserted against a fixed clock across every policy
branch; the concurrent Reconciler is driven through a fake Actions with
injected clock/sleep seams to cover backoff, cooldown, grace, and every error
path deterministically.
COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1 # 100.0%
BSD-3-Clause — see LICENSE. Copyright the go-proc/respawn authors.