You've already forked agentic-coding-workflow
feat(jobs): emit job.enqueue + worktree.pruned audit rows
Deferred audit retrofits from Plan #5 self-review: - WebhookNotifier now records a job.enqueue audit row after each successful repo.Enqueue (best-effort: failures are warn-logged, not propagated). Added optional audit.Recorder + *slog.Logger params to NewWebhookNotifier. - WorktreePrune.executionPhase now records a worktree.pruned audit row after each successful FinishPruneSuccess. Added optional audit.Recorder param to NewWorktreePrune. - app.go wires the existing auditRec into both constructors. - integration_test.go: drop the _ = auditRec suppression and pass auditRec into the webhook notifier so the closed-loop test exercises the audit emit path.
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yan1h/agent-coding-workflow/internal/audit"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/infra/notify"
|
||||
)
|
||||
|
||||
@@ -45,19 +46,21 @@ type WorktreePruneConfig struct {
|
||||
|
||||
// WorktreePrune is the B runner.
|
||||
type WorktreePrune struct {
|
||||
repo WorktreePruneRepo
|
||||
gitr WorktreePruner
|
||||
disp Dispatcher
|
||||
cfg WorktreePruneConfig
|
||||
log *slog.Logger
|
||||
repo WorktreePruneRepo
|
||||
gitr WorktreePruner
|
||||
disp Dispatcher
|
||||
cfg WorktreePruneConfig
|
||||
log *slog.Logger
|
||||
audit audit.Recorder
|
||||
}
|
||||
|
||||
// NewWorktreePrune constructs the runner.
|
||||
func NewWorktreePrune(repo WorktreePruneRepo, gitr WorktreePruner, disp Dispatcher, cfg WorktreePruneConfig, log *slog.Logger) *WorktreePrune {
|
||||
// NewWorktreePrune constructs the runner. rec is optional; when non-nil the
|
||||
// runner emits a worktree.pruned audit row after each successful prune.
|
||||
func NewWorktreePrune(repo WorktreePruneRepo, gitr WorktreePruner, disp Dispatcher, cfg WorktreePruneConfig, log *slog.Logger, rec audit.Recorder) *WorktreePrune {
|
||||
if log == nil {
|
||||
log = slog.Default()
|
||||
}
|
||||
return &WorktreePrune{repo: repo, gitr: gitr, disp: disp, cfg: cfg, log: log}
|
||||
return &WorktreePrune{repo: repo, gitr: gitr, disp: disp, cfg: cfg, log: log, audit: rec}
|
||||
}
|
||||
|
||||
// Run executes warning phase then execution phase.
|
||||
@@ -117,6 +120,19 @@ func (r *WorktreePrune) executionPhase(ctx context.Context) {
|
||||
}
|
||||
if err := r.repo.FinishPruneSuccess(ctx, w.ID); err != nil {
|
||||
r.log.Error("worktree_prune.finish_success", "worktree_id", w.ID, "err", err.Error())
|
||||
continue
|
||||
}
|
||||
if r.audit != nil {
|
||||
if rerr := r.audit.Record(ctx, audit.Entry{
|
||||
Action: "worktree.pruned",
|
||||
TargetType: "worktree",
|
||||
TargetID: w.ID.String(),
|
||||
Metadata: map[string]any{
|
||||
"branch": w.Branch,
|
||||
},
|
||||
}); rerr != nil {
|
||||
r.log.Warn("worktree_prune.audit_failed", "worktree_id", w.ID, "err", rerr.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ func TestWorktreePrune_WarningPhase(t *testing.T) {
|
||||
gitr := &fakePruner{}
|
||||
cfg := runners.WorktreePruneConfig{IdleThreshold: 7 * 24 * time.Hour, WarningLead: 3 * 24 * time.Hour}
|
||||
|
||||
r := runners.NewWorktreePrune(repo, gitr, disp, cfg, discardLogger())
|
||||
r := runners.NewWorktreePrune(repo, gitr, disp, cfg, discardLogger(), nil)
|
||||
r.Run(context.Background())
|
||||
|
||||
require.Len(t, repo.markedWarning, 1)
|
||||
@@ -124,7 +124,7 @@ func TestWorktreePrune_ExecutionPhaseSuccess(t *testing.T) {
|
||||
gitr := &fakePruner{}
|
||||
cfg := runners.WorktreePruneConfig{IdleThreshold: 7 * 24 * time.Hour, WarningLead: 3 * 24 * time.Hour}
|
||||
|
||||
r := runners.NewWorktreePrune(repo, gitr, &recordingDispatcher{}, cfg, discardLogger())
|
||||
r := runners.NewWorktreePrune(repo, gitr, &recordingDispatcher{}, cfg, discardLogger(), nil)
|
||||
r.Run(context.Background())
|
||||
|
||||
assert.True(t, repo.tryStartCalled[id])
|
||||
@@ -146,7 +146,7 @@ func TestWorktreePrune_ExecutionFailureRollsBack(t *testing.T) {
|
||||
gitr := &fakePruner{err: errors.New("git worktree remove failed")}
|
||||
cfg := runners.WorktreePruneConfig{IdleThreshold: 7 * 24 * time.Hour, WarningLead: 3 * 24 * time.Hour}
|
||||
|
||||
r := runners.NewWorktreePrune(repo, gitr, &recordingDispatcher{}, cfg, discardLogger())
|
||||
r := runners.NewWorktreePrune(repo, gitr, &recordingDispatcher{}, cfg, discardLogger(), nil)
|
||||
r.Run(context.Background())
|
||||
|
||||
assert.True(t, repo.tryStartCalled[id])
|
||||
@@ -162,7 +162,7 @@ func TestWorktreePrune_NoRowsIsNoOp(t *testing.T) {
|
||||
disp := &recordingDispatcher{}
|
||||
cfg := runners.WorktreePruneConfig{IdleThreshold: 7 * 24 * time.Hour, WarningLead: 3 * 24 * time.Hour}
|
||||
|
||||
r := runners.NewWorktreePrune(repo, gitr, disp, cfg, discardLogger())
|
||||
r := runners.NewWorktreePrune(repo, gitr, disp, cfg, discardLogger(), nil)
|
||||
r.Run(context.Background())
|
||||
|
||||
assert.Empty(t, repo.markedWarning)
|
||||
@@ -185,7 +185,7 @@ func TestWorktreePrune_CASLostSkipsSilently(t *testing.T) {
|
||||
gitr := &fakePruner{}
|
||||
cfg := runners.WorktreePruneConfig{IdleThreshold: 7 * 24 * time.Hour, WarningLead: 3 * 24 * time.Hour}
|
||||
|
||||
r := runners.NewWorktreePrune(repo, gitr, &recordingDispatcher{}, cfg, discardLogger())
|
||||
r := runners.NewWorktreePrune(repo, gitr, &recordingDispatcher{}, cfg, discardLogger(), nil)
|
||||
r.Run(context.Background())
|
||||
|
||||
// TryStartPrune was called but failed.
|
||||
|
||||
Reference in New Issue
Block a user