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:
2026-05-06 13:35:45 +08:00
parent f6089ac5fb
commit b1ccce567e
6 changed files with 65 additions and 28 deletions
+24 -8
View File
@@ -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())
}
}
}
}