Documentation
¶
Overview ¶
Package autofix holds the whitelist of corrective actions pulse may invoke without further human approval. Every autofix must be:
- idempotent: running it twice in a row produces the same end state;
- scoped: touches only files pulse understands to be its own;
- bounded: completes in under a minute on typical workspaces;
- safe-on-failure: partial failures leave the workspace in a consistent state (no half-deleted, half-renamed, half-compressed files).
New autofixes are added by implementing the Fixer interface and registering a constructor in the DefaultRegistry initializer below. The decider cannot invoke an autofix whose name is not in both the code registry AND the operator's config allow-list — this double gate is intentional.
Index ¶
- Constants
- type AutoContinueChat
- type AutoContinueGoalPlan
- type AutoResumeFailedChat
- type ChatAutoContinueResult
- type ChatAutoContinuer
- type CleanupStaleTmp
- type CompressOldLogs
- type ErrUnknown
- type FailedChatResumeResult
- type FailedChatResumer
- type Fixer
- type GoalPlanAutoContinueResult
- type GoalPlanAutoContinuer
- type Registry
- type Result
Constants ¶
const AutoContinueChatName = "auto_continue_chat"
const AutoContinueGoalPlanName = "auto_continue_goal_plan"
const AutoResumeFailedChatName = "auto_resume_failed_chat"
const CleanupStaleTmpName = "cleanup_stale_tmp"
CleanupStaleTmpName is the canonical name used in configs and logs.
const CompressOldLogsName = "compress_old_logs"
CompressOldLogsName is the canonical name used in configs and logs.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AutoContinueChat ¶ added in v0.31.114
type AutoContinueChat struct {
Continuer ChatAutoContinuer
}
func (*AutoContinueChat) Name ¶ added in v0.31.114
func (a *AutoContinueChat) Name() string
type AutoContinueGoalPlan ¶ added in v0.31.145
type AutoContinueGoalPlan struct {
Continuer GoalPlanAutoContinuer
}
AutoContinueGoalPlan runs one chat turn per session whose plan has just completed with auto-continue opted in. The turn asks the LLM to either declare the session goal achieved (which terminates the loop) or propose the next plan. A hard iteration cap on the Plan keeps the loop bounded.
func (*AutoContinueGoalPlan) Name ¶ added in v0.31.145
func (a *AutoContinueGoalPlan) Name() string
type AutoResumeFailedChat ¶ added in v0.31.144
type AutoResumeFailedChat struct {
Resumer FailedChatResumer
}
AutoResumeFailedChat retries chat turns that halted with a recoverable failure (tool error from a non-mutating tool, or a user message the LLM never finished responding to). Mutating-tool failures are filtered out at detection time, so by the time a candidate reaches this autofix it is safe to re-run the turn.
func (*AutoResumeFailedChat) Name ¶ added in v0.31.144
func (a *AutoResumeFailedChat) Name() string
type ChatAutoContinueResult ¶ added in v0.31.114
type ChatAutoContinuer ¶ added in v0.31.114
type ChatAutoContinuer interface {
AutoContinueStalledChats(ctx context.Context) (ChatAutoContinueResult, error)
}
type CleanupStaleTmp ¶
type CleanupStaleTmp struct {
// Dirs is the list of absolute tmp paths to scan. Non-existent
// entries are silently skipped. Typical values:
// <workspace>/tmp
// <workspace>/_shared/tmp
Dirs []string
// MaxAge is the minimum file age required before deletion. Zero
// falls back to 7 days, matching the Phase 1 default.
MaxAge time.Duration
// Now is injectable for tests; real code uses time.Now.
Now func() time.Time
}
CleanupStaleTmp removes plain files older than MaxAge from the workspace's temporary directories. It intentionally DOES NOT:
- follow symlinks (they are skipped; pulse must never chase links out of workspace-controlled paths);
- delete directories (even empty ones — directory cleanup is a separate concern and can remove user state accidentally);
- descend into subdirectories (recursion widens the blast radius for a background agent; tmp layouts are expected to be flat);
The fixer is idempotent: a second run in quick succession finds no candidates and reports Changed=false.
func (*CleanupStaleTmp) Name ¶
func (c *CleanupStaleTmp) Name() string
type CompressOldLogs ¶
type CompressOldLogs struct {
// LogsDir is an absolute path to the directory being scanned. It is
// usually <workspace>/logs. Non-existent dirs are treated as empty.
LogsDir string
// MaxAge is the minimum file age required before compression. Zero
// falls back to 7 days, matching the Phase 1 default.
MaxAge time.Duration
// Now is injectable for tests; real code uses time.Now.
Now func() time.Time
}
CompressOldLogs gzips .log files older than MaxAge found under the workspace logs directory. It is designed to run under the pulse watchdog as a periodic housekeeping fix, so its touch is narrow:
- only files ending in ".log" (not ".log.gz", not other extensions)
- only files with modification time older than MaxAge
- compressed output is written alongside as "<file>.log.gz"
- original is removed only after successful compression and fsync
The fixer is idempotent: a second run finds no candidates (they are already gzipped) and reports Changed=false.
func (*CompressOldLogs) Name ¶
func (c *CompressOldLogs) Name() string
type ErrUnknown ¶
type ErrUnknown struct{ Name string }
ErrUnknown is returned when a requested autofix is not registered.
func (ErrUnknown) Error ¶
func (e ErrUnknown) Error() string
type FailedChatResumeResult ¶ added in v0.31.144
type FailedChatResumer ¶ added in v0.31.144
type FailedChatResumer interface {
ResumeFailedChats(ctx context.Context) (FailedChatResumeResult, error)
}
type Fixer ¶
Fixer is the single-method interface each autofix implementation must satisfy. Fixers are expected to be pure functions of their constructor arguments (workspace dir, clock, etc.) — no package-level state.
type GoalPlanAutoContinueResult ¶ added in v0.31.145
type GoalPlanAutoContinuer ¶ added in v0.31.145
type GoalPlanAutoContinuer interface {
ContinueGoalPlans(ctx context.Context) (GoalPlanAutoContinueResult, error)
}
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is a concurrent-safe name → Fixer map. Its primary use is the pulse runtime looking up a named autofix from a Decision.
func (*Registry) AllowedIntersection ¶
AllowedIntersection returns the intersection of the registry's names and the operator-configured allow-list. This is the exact list that should be handed to the decider as the autofix policy — anything outside this set cannot safely run.
func (*Registry) Names ¶
Names returns all registered fixer names in sorted order. Used by pulse startup logging and by HTTP handlers that want to report which autofixes are known.
type Result ¶
type Result struct {
Name string `json:"name"`
Summary string `json:"summary,omitempty"`
Details map[string]any `json:"details,omitempty"`
Changed bool `json:"changed"`
}
Result captures what an autofix did. Fields are optional; an empty Result is valid for autofixes that have nothing meaningful to report (e.g. "there was nothing to clean up").