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 ¶
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 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 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 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").