fix: Address golangci-lint errors (errcheck, gosec) (#76)
Apply PR #76 from dannomayernotabot: - Add golangci exclusions for internal package false positives - Tighten file permissions (0644 -> 0600) for sensitive files - Add ReadHeaderTimeout to HTTP server (slowloris prevention) - Explicit error ignoring with _ = for intentional cases - Add //nolint comments with justifications - Spelling: cancelled -> canceled (US locale) Co-Authored-By: dannomayernotabot <noreply@github.com> 🤖 Generated with Claude Code
This commit is contained in:
@@ -215,10 +215,10 @@ func (e *Engineer) ProcessMR(ctx context.Context, mr *beads.Issue) ProcessResult
|
||||
}
|
||||
|
||||
// Log what we're processing
|
||||
fmt.Fprintln(e.output, "[Engineer] Processing MR:")
|
||||
fmt.Fprintf(e.output, " Branch: %s\n", mrFields.Branch)
|
||||
fmt.Fprintf(e.output, " Target: %s\n", mrFields.Target)
|
||||
fmt.Fprintf(e.output, " Worker: %s\n", mrFields.Worker)
|
||||
_, _ = fmt.Fprintln(e.output, "[Engineer] Processing MR:")
|
||||
_, _ = fmt.Fprintf(e.output, " Branch: %s\n", mrFields.Branch)
|
||||
_, _ = fmt.Fprintf(e.output, " Target: %s\n", mrFields.Target)
|
||||
_, _ = fmt.Fprintf(e.output, " Worker: %s\n", mrFields.Worker)
|
||||
|
||||
return e.doMerge(ctx, mrFields.Branch, mrFields.Target, mrFields.SourceIssue)
|
||||
}
|
||||
@@ -227,7 +227,7 @@ func (e *Engineer) ProcessMR(ctx context.Context, mr *beads.Issue) ProcessResult
|
||||
// This is the core merge logic shared by ProcessMR and ProcessMRFromQueue.
|
||||
func (e *Engineer) doMerge(ctx context.Context, branch, target, sourceIssue string) ProcessResult {
|
||||
// Step 1: Fetch the source branch from origin
|
||||
fmt.Fprintf(e.output, "[Engineer] Fetching branch %s from origin...\n", branch)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Fetching branch %s from origin...\n", branch)
|
||||
if err := e.git.FetchBranch("origin", branch); err != nil {
|
||||
return ProcessResult{
|
||||
Success: false,
|
||||
@@ -236,7 +236,7 @@ func (e *Engineer) doMerge(ctx context.Context, branch, target, sourceIssue stri
|
||||
}
|
||||
|
||||
// Step 2: Checkout the target branch
|
||||
fmt.Fprintf(e.output, "[Engineer] Checking out target branch %s...\n", target)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Checking out target branch %s...\n", target)
|
||||
if err := e.git.Checkout(target); err != nil {
|
||||
return ProcessResult{
|
||||
Success: false,
|
||||
@@ -247,11 +247,11 @@ func (e *Engineer) doMerge(ctx context.Context, branch, target, sourceIssue stri
|
||||
// Make sure target is up to date with origin
|
||||
if err := e.git.Pull("origin", target); err != nil {
|
||||
// Pull might fail if nothing to pull, that's ok
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: pull from origin/%s: %v (continuing)\n", target, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: pull from origin/%s: %v (continuing)\n", target, err)
|
||||
}
|
||||
|
||||
// Step 3: Check for merge conflicts
|
||||
fmt.Fprintf(e.output, "[Engineer] Checking for conflicts...\n")
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Checking for conflicts...\n")
|
||||
remoteBranch := "origin/" + branch
|
||||
conflicts, err := e.git.CheckConflicts(remoteBranch, target)
|
||||
if err != nil {
|
||||
@@ -271,7 +271,7 @@ func (e *Engineer) doMerge(ctx context.Context, branch, target, sourceIssue stri
|
||||
|
||||
// Step 4: Run tests if configured
|
||||
if e.config.RunTests && e.config.TestCommand != "" {
|
||||
fmt.Fprintf(e.output, "[Engineer] Running tests: %s\n", e.config.TestCommand)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Running tests: %s\n", e.config.TestCommand)
|
||||
result := e.runTests(ctx)
|
||||
if !result.Success {
|
||||
return ProcessResult{
|
||||
@@ -280,7 +280,7 @@ func (e *Engineer) doMerge(ctx context.Context, branch, target, sourceIssue stri
|
||||
Error: result.Error,
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(e.output, "[Engineer] Tests passed")
|
||||
_, _ = fmt.Fprintln(e.output, "[Engineer] Tests passed")
|
||||
}
|
||||
|
||||
// Step 5: Perform the actual merge
|
||||
@@ -288,7 +288,7 @@ func (e *Engineer) doMerge(ctx context.Context, branch, target, sourceIssue stri
|
||||
if sourceIssue != "" {
|
||||
mergeMsg = fmt.Sprintf("Merge %s into %s (%s)", branch, target, sourceIssue)
|
||||
}
|
||||
fmt.Fprintf(e.output, "[Engineer] Merging with message: %s\n", mergeMsg)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Merging with message: %s\n", mergeMsg)
|
||||
if err := e.git.MergeNoFF(remoteBranch, mergeMsg); err != nil {
|
||||
if errors.Is(err, git.ErrMergeConflict) {
|
||||
_ = e.git.AbortMerge()
|
||||
@@ -314,7 +314,7 @@ func (e *Engineer) doMerge(ctx context.Context, branch, target, sourceIssue stri
|
||||
}
|
||||
|
||||
// Step 7: Push to origin
|
||||
fmt.Fprintf(e.output, "[Engineer] Pushing to origin/%s...\n", target)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Pushing to origin/%s...\n", target)
|
||||
if err := e.git.Push("origin", target, false); err != nil {
|
||||
return ProcessResult{
|
||||
Success: false,
|
||||
@@ -322,7 +322,7 @@ func (e *Engineer) doMerge(ctx context.Context, branch, target, sourceIssue stri
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(e.output, "[Engineer] Successfully merged: %s\n", mergeCommit[:8])
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Successfully merged: %s\n", mergeCommit[:8])
|
||||
return ProcessResult{
|
||||
Success: true,
|
||||
MergeCommit: mergeCommit,
|
||||
@@ -344,12 +344,12 @@ func (e *Engineer) runTests(ctx context.Context) ProcessResult {
|
||||
var lastErr error
|
||||
for attempt := 1; attempt <= maxRetries; attempt++ {
|
||||
if attempt > 1 {
|
||||
fmt.Fprintf(e.output, "[Engineer] Retrying tests (attempt %d/%d)...\n", attempt, maxRetries)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Retrying tests (attempt %d/%d)...\n", attempt, maxRetries)
|
||||
}
|
||||
|
||||
// Note: TestCommand comes from rig's config.json (trusted infrastructure config),
|
||||
// not from PR branches. Shell execution is intentional for flexibility (pipes, etc).
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", e.config.TestCommand)
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", e.config.TestCommand) //nolint:gosec // G204: TestCommand is from trusted rig config
|
||||
cmd.Dir = e.workDir
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
@@ -361,11 +361,11 @@ func (e *Engineer) runTests(ctx context.Context) ProcessResult {
|
||||
}
|
||||
lastErr = err
|
||||
|
||||
// Check if context was cancelled
|
||||
// Check if context was canceled
|
||||
if ctx.Err() != nil {
|
||||
return ProcessResult{
|
||||
Success: false,
|
||||
Error: "test run cancelled",
|
||||
Error: "test run canceled",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,42 +396,42 @@ func (e *Engineer) handleSuccess(mr *beads.Issue, result ProcessResult) {
|
||||
mrFields.CloseReason = "merged"
|
||||
newDesc := beads.SetMRFields(mr, mrFields)
|
||||
if err := e.beads.Update(mr.ID, beads.UpdateOptions{Description: &newDesc}); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to update MR %s with merge commit: %v\n", mr.ID, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to update MR %s with merge commit: %v\n", mr.ID, err)
|
||||
}
|
||||
|
||||
// 2. Close MR with reason 'merged'
|
||||
if err := e.beads.CloseWithReason("merged", mr.ID); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to close MR %s: %v\n", mr.ID, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to close MR %s: %v\n", mr.ID, err)
|
||||
}
|
||||
|
||||
// 3. Close source issue with reference to MR
|
||||
if mrFields.SourceIssue != "" {
|
||||
closeReason := fmt.Sprintf("Merged in %s", mr.ID)
|
||||
if err := e.beads.CloseWithReason(closeReason, mrFields.SourceIssue); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to close source issue %s: %v\n", mrFields.SourceIssue, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to close source issue %s: %v\n", mrFields.SourceIssue, err)
|
||||
} else {
|
||||
fmt.Fprintf(e.output, "[Engineer] Closed source issue: %s\n", mrFields.SourceIssue)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Closed source issue: %s\n", mrFields.SourceIssue)
|
||||
}
|
||||
}
|
||||
|
||||
// 3.5. Clear agent bead's active_mr reference (traceability cleanup)
|
||||
if mrFields.AgentBead != "" {
|
||||
if err := e.beads.UpdateAgentActiveMR(mrFields.AgentBead, ""); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to clear agent bead %s active_mr: %v\n", mrFields.AgentBead, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to clear agent bead %s active_mr: %v\n", mrFields.AgentBead, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Delete source branch if configured (local only - branches never go to origin)
|
||||
if e.config.DeleteMergedBranches && mrFields.Branch != "" {
|
||||
if err := e.git.DeleteBranch(mrFields.Branch, true); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to delete branch %s: %v\n", mrFields.Branch, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to delete branch %s: %v\n", mrFields.Branch, err)
|
||||
} else {
|
||||
fmt.Fprintf(e.output, "[Engineer] Deleted local branch: %s\n", mrFields.Branch)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Deleted local branch: %s\n", mrFields.Branch)
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Log success
|
||||
fmt.Fprintf(e.output, "[Engineer] ✓ Merged: %s (commit: %s)\n", mr.ID, result.MergeCommit)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] ✓ Merged: %s (commit: %s)\n", mr.ID, result.MergeCommit)
|
||||
}
|
||||
|
||||
// handleFailure handles a failed merge request.
|
||||
@@ -440,25 +440,25 @@ func (e *Engineer) handleFailure(mr *beads.Issue, result ProcessResult) {
|
||||
// Reopen the MR (back to open status for rework)
|
||||
open := "open"
|
||||
if err := e.beads.Update(mr.ID, beads.UpdateOptions{Status: &open}); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to reopen MR %s: %v\n", mr.ID, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to reopen MR %s: %v\n", mr.ID, err)
|
||||
}
|
||||
|
||||
// Log the failure
|
||||
fmt.Fprintf(e.output, "[Engineer] ✗ Failed: %s - %s\n", mr.ID, result.Error)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] ✗ Failed: %s - %s\n", mr.ID, result.Error)
|
||||
}
|
||||
|
||||
// ProcessMRFromQueue processes a merge request from wisp queue.
|
||||
func (e *Engineer) ProcessMRFromQueue(ctx context.Context, mr *mrqueue.MR) ProcessResult {
|
||||
// MR fields are directly on the struct (no parsing needed)
|
||||
fmt.Fprintln(e.output, "[Engineer] Processing MR from queue:")
|
||||
fmt.Fprintf(e.output, " Branch: %s\n", mr.Branch)
|
||||
fmt.Fprintf(e.output, " Target: %s\n", mr.Target)
|
||||
fmt.Fprintf(e.output, " Worker: %s\n", mr.Worker)
|
||||
fmt.Fprintf(e.output, " Source: %s\n", mr.SourceIssue)
|
||||
_, _ = fmt.Fprintln(e.output, "[Engineer] Processing MR from queue:")
|
||||
_, _ = fmt.Fprintf(e.output, " Branch: %s\n", mr.Branch)
|
||||
_, _ = fmt.Fprintf(e.output, " Target: %s\n", mr.Target)
|
||||
_, _ = fmt.Fprintf(e.output, " Worker: %s\n", mr.Worker)
|
||||
_, _ = fmt.Fprintf(e.output, " Source: %s\n", mr.SourceIssue)
|
||||
|
||||
// Emit merge_started event
|
||||
if err := e.eventLogger.LogMergeStarted(mr); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to log merge_started event: %v\n", err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to log merge_started event: %v\n", err)
|
||||
}
|
||||
|
||||
// Use the shared merge logic
|
||||
@@ -469,7 +469,7 @@ func (e *Engineer) ProcessMRFromQueue(ctx context.Context, mr *mrqueue.MR) Proce
|
||||
func (e *Engineer) handleSuccessFromQueue(mr *mrqueue.MR, result ProcessResult) {
|
||||
// Emit merged event
|
||||
if err := e.eventLogger.LogMerged(mr, result.MergeCommit); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to log merged event: %v\n", err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to log merged event: %v\n", err)
|
||||
}
|
||||
|
||||
// Release merge slot if this was a conflict resolution
|
||||
@@ -480,10 +480,10 @@ func (e *Engineer) handleSuccessFromQueue(mr *mrqueue.MR, result ProcessResult)
|
||||
// Only log if it seems like an actual issue
|
||||
errStr := err.Error()
|
||||
if !strings.Contains(errStr, "not held") && !strings.Contains(errStr, "not found") {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to release merge slot: %v\n", err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to release merge slot: %v\n", err)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(e.output, "[Engineer] Released merge slot\n")
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Released merge slot\n")
|
||||
}
|
||||
|
||||
// Update and close the MR bead (matches handleSuccess behavior)
|
||||
@@ -491,7 +491,7 @@ func (e *Engineer) handleSuccessFromQueue(mr *mrqueue.MR, result ProcessResult)
|
||||
// Fetch the MR bead to update its fields
|
||||
mrBead, err := e.beads.Show(mr.ID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to fetch MR bead %s: %v\n", mr.ID, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to fetch MR bead %s: %v\n", mr.ID, err)
|
||||
} else {
|
||||
// Update MR with merge_commit SHA and close_reason
|
||||
mrFields := beads.ParseMRFields(mrBead)
|
||||
@@ -502,15 +502,15 @@ func (e *Engineer) handleSuccessFromQueue(mr *mrqueue.MR, result ProcessResult)
|
||||
mrFields.CloseReason = "merged"
|
||||
newDesc := beads.SetMRFields(mrBead, mrFields)
|
||||
if err := e.beads.Update(mr.ID, beads.UpdateOptions{Description: &newDesc}); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to update MR %s with merge commit: %v\n", mr.ID, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to update MR %s with merge commit: %v\n", mr.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Close MR bead with reason 'merged'
|
||||
if err := e.beads.CloseWithReason("merged", mr.ID); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to close MR %s: %v\n", mr.ID, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to close MR %s: %v\n", mr.ID, err)
|
||||
} else {
|
||||
fmt.Fprintf(e.output, "[Engineer] Closed MR bead: %s\n", mr.ID)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Closed MR bead: %s\n", mr.ID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,35 +518,35 @@ func (e *Engineer) handleSuccessFromQueue(mr *mrqueue.MR, result ProcessResult)
|
||||
if mr.SourceIssue != "" {
|
||||
closeReason := fmt.Sprintf("Merged in %s", mr.ID)
|
||||
if err := e.beads.CloseWithReason(closeReason, mr.SourceIssue); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to close source issue %s: %v\n", mr.SourceIssue, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to close source issue %s: %v\n", mr.SourceIssue, err)
|
||||
} else {
|
||||
fmt.Fprintf(e.output, "[Engineer] Closed source issue: %s\n", mr.SourceIssue)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Closed source issue: %s\n", mr.SourceIssue)
|
||||
}
|
||||
}
|
||||
|
||||
// 1.5. Clear agent bead's active_mr reference (traceability cleanup)
|
||||
if mr.AgentBead != "" {
|
||||
if err := e.beads.UpdateAgentActiveMR(mr.AgentBead, ""); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to clear agent bead %s active_mr: %v\n", mr.AgentBead, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to clear agent bead %s active_mr: %v\n", mr.AgentBead, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Delete source branch if configured (local only)
|
||||
if e.config.DeleteMergedBranches && mr.Branch != "" {
|
||||
if err := e.git.DeleteBranch(mr.Branch, true); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to delete branch %s: %v\n", mr.Branch, err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to delete branch %s: %v\n", mr.Branch, err)
|
||||
} else {
|
||||
fmt.Fprintf(e.output, "[Engineer] Deleted local branch: %s\n", mr.Branch)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Deleted local branch: %s\n", mr.Branch)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Remove MR from queue (ephemeral - just delete the file)
|
||||
if err := e.mrQueue.Remove(mr.ID); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to remove MR from queue: %v\n", err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to remove MR from queue: %v\n", err)
|
||||
}
|
||||
|
||||
// 4. Log success
|
||||
fmt.Fprintf(e.output, "[Engineer] ✓ Merged: %s (commit: %s)\n", mr.ID, result.MergeCommit)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] ✓ Merged: %s (commit: %s)\n", mr.ID, result.MergeCommit)
|
||||
}
|
||||
|
||||
// handleFailureFromQueue handles a failed merge from wisp queue.
|
||||
@@ -555,7 +555,7 @@ func (e *Engineer) handleSuccessFromQueue(mr *mrqueue.MR, result ProcessResult)
|
||||
func (e *Engineer) handleFailureFromQueue(mr *mrqueue.MR, result ProcessResult) {
|
||||
// Emit merge_failed event
|
||||
if err := e.eventLogger.LogMergeFailed(mr, result.Error); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to log merge_failed event: %v\n", err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to log merge_failed event: %v\n", err)
|
||||
}
|
||||
|
||||
// If this was a conflict, create a conflict-resolution task for dispatch
|
||||
@@ -563,24 +563,24 @@ func (e *Engineer) handleFailureFromQueue(mr *mrqueue.MR, result ProcessResult)
|
||||
if result.Conflict {
|
||||
taskID, err := e.createConflictResolutionTask(mr, result)
|
||||
if err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to create conflict resolution task: %v\n", err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to create conflict resolution task: %v\n", err)
|
||||
} else {
|
||||
// Block the MR on the conflict resolution task
|
||||
// When the task closes, the MR unblocks and re-enters the ready queue
|
||||
if err := e.mrQueue.SetBlockedBy(mr.ID, taskID); err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: failed to block MR on task: %v\n", err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to block MR on task: %v\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(e.output, "[Engineer] MR %s blocked on conflict task %s (non-blocking delegation)\n", mr.ID, taskID)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] MR %s blocked on conflict task %s (non-blocking delegation)\n", mr.ID, taskID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log the failure - MR stays in queue but may be blocked
|
||||
fmt.Fprintf(e.output, "[Engineer] ✗ Failed: %s - %s\n", mr.ID, result.Error)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] ✗ Failed: %s - %s\n", mr.ID, result.Error)
|
||||
if mr.BlockedBy != "" {
|
||||
fmt.Fprintln(e.output, "[Engineer] MR blocked pending conflict resolution - queue continues to next MR")
|
||||
_, _ = fmt.Fprintln(e.output, "[Engineer] MR blocked pending conflict resolution - queue continues to next MR")
|
||||
} else {
|
||||
fmt.Fprintln(e.output, "[Engineer] MR remains in queue for retry")
|
||||
_, _ = fmt.Fprintln(e.output, "[Engineer] MR remains in queue for retry")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,29 +600,29 @@ func (e *Engineer) handleFailureFromQueue(mr *mrqueue.MR, result ProcessResult)
|
||||
// This serializes conflict resolution - only one polecat can resolve conflicts at a time.
|
||||
// If the slot is already held, we skip creating the task and let the MR stay in queue.
|
||||
// When the current resolution completes and merges, the slot is released.
|
||||
func (e *Engineer) createConflictResolutionTask(mr *mrqueue.MR, result ProcessResult) (string, error) {
|
||||
func (e *Engineer) createConflictResolutionTask(mr *mrqueue.MR, _ ProcessResult) (string, error) { // result unused but kept for future merge diagnostics
|
||||
// === MERGE SLOT GATE: Serialize conflict resolution ===
|
||||
// Ensure merge slot exists (idempotent)
|
||||
slotID, err := e.beads.MergeSlotEnsureExists()
|
||||
if err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: could not ensure merge slot: %v\n", err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: could not ensure merge slot: %v\n", err)
|
||||
// Continue anyway - slot is optional for now
|
||||
} else {
|
||||
// Try to acquire the merge slot
|
||||
holder := e.rig.Name + "/refinery"
|
||||
status, err := e.beads.MergeSlotAcquire(holder, false)
|
||||
if err != nil {
|
||||
fmt.Fprintf(e.output, "[Engineer] Warning: could not acquire merge slot: %v\n", err)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: could not acquire merge slot: %v\n", err)
|
||||
// Continue anyway - slot is optional
|
||||
} else if !status.Available && status.Holder != "" && status.Holder != holder {
|
||||
// Slot is held by someone else - skip creating the task
|
||||
// The MR stays in queue and will retry when slot is released
|
||||
fmt.Fprintf(e.output, "[Engineer] Merge slot held by %s - deferring conflict resolution\n", status.Holder)
|
||||
fmt.Fprintf(e.output, "[Engineer] MR %s will retry after current resolution completes\n", mr.ID)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Merge slot held by %s - deferring conflict resolution\n", status.Holder)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] MR %s will retry after current resolution completes\n", mr.ID)
|
||||
return "", nil // Not an error - just deferred
|
||||
}
|
||||
// Either we acquired the slot, or status indicates we already hold it
|
||||
fmt.Fprintf(e.output, "[Engineer] Acquired merge slot: %s\n", slotID)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Acquired merge slot: %s\n", slotID)
|
||||
}
|
||||
|
||||
// Get the current main SHA for conflict tracking
|
||||
@@ -694,7 +694,7 @@ The Refinery will automatically retry the merge after you force-push.`,
|
||||
// The conflict task's ID is returned so the MR can be blocked on it.
|
||||
// When the task closes, the MR unblocks and re-enters the ready queue.
|
||||
|
||||
fmt.Fprintf(e.output, "[Engineer] Created conflict resolution task: %s (P%d)\n", task.ID, task.Priority)
|
||||
_, _ = fmt.Fprintf(e.output, "[Engineer] Created conflict resolution task: %s (P%d)\n", task.ID, task.Priority)
|
||||
|
||||
// Update the MR's retry count for priority scoring
|
||||
mr.RetryCount = retryCount
|
||||
|
||||
@@ -143,7 +143,7 @@ func (m *Manager) Start(foreground bool) error {
|
||||
return ErrAlreadyRunning
|
||||
}
|
||||
// Zombie - tmux alive but Claude dead. Kill and recreate.
|
||||
fmt.Fprintln(m.output, "⚠ Detected zombie session (tmux alive, Claude dead). Recreating...")
|
||||
_, _ = fmt.Fprintln(m.output, "⚠ Detected zombie session (tmux alive, Claude dead). Recreating...")
|
||||
if err := t.KillSession(sessionID); err != nil {
|
||||
return fmt.Errorf("killing zombie session: %w", err)
|
||||
}
|
||||
@@ -402,15 +402,15 @@ func parseTime(s string) time.Time {
|
||||
// run is deprecated - foreground mode now just prints a message.
|
||||
// The Refinery agent (Claude) handles all merge processing.
|
||||
// See: ZFC #5 - Move merge/conflict decisions from Go to Refinery agent
|
||||
func (m *Manager) run(ref *Refinery) error {
|
||||
fmt.Fprintln(m.output, "")
|
||||
fmt.Fprintln(m.output, "╔══════════════════════════════════════════════════════════════╗")
|
||||
fmt.Fprintln(m.output, "║ Foreground mode is deprecated. ║")
|
||||
fmt.Fprintln(m.output, "║ ║")
|
||||
fmt.Fprintln(m.output, "║ The Refinery agent (Claude) handles all merge decisions. ║")
|
||||
fmt.Fprintln(m.output, "║ Use 'gt refinery start' to run in background mode. ║")
|
||||
fmt.Fprintln(m.output, "╚══════════════════════════════════════════════════════════════╝")
|
||||
fmt.Fprintln(m.output, "")
|
||||
func (m *Manager) run(_ *Refinery) error { // ref unused: deprecated function
|
||||
_, _ = fmt.Fprintln(m.output, "")
|
||||
_, _ = fmt.Fprintln(m.output, "╔══════════════════════════════════════════════════════════════╗")
|
||||
_, _ = fmt.Fprintln(m.output, "║ Foreground mode is deprecated. ║")
|
||||
_, _ = fmt.Fprintln(m.output, "║ ║")
|
||||
_, _ = fmt.Fprintln(m.output, "║ The Refinery agent (Claude) handles all merge decisions. ║")
|
||||
_, _ = fmt.Fprintln(m.output, "║ Use 'gt refinery start' to run in background mode. ║")
|
||||
_, _ = fmt.Fprintln(m.output, "╚══════════════════════════════════════════════════════════════╝")
|
||||
_, _ = fmt.Fprintln(m.output, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -458,7 +458,7 @@ func (m *Manager) completeMR(mr *MergeRequest, closeReason CloseReason, errMsg s
|
||||
// Close the MR (in_progress → closed)
|
||||
if err := mr.Close(closeReason); err != nil {
|
||||
// Log error but continue - this shouldn't happen
|
||||
fmt.Fprintf(m.output, "Warning: failed to close MR: %v\n", err)
|
||||
_, _ = fmt.Fprintf(m.output, "Warning: failed to close MR: %v\n", err)
|
||||
}
|
||||
switch closeReason {
|
||||
case CloseReasonMerged:
|
||||
@@ -471,7 +471,7 @@ func (m *Manager) completeMR(mr *MergeRequest, closeReason CloseReason, errMsg s
|
||||
// Reopen the MR for rework (in_progress → open)
|
||||
if err := mr.Reopen(); err != nil {
|
||||
// Log error but continue
|
||||
fmt.Fprintf(m.output, "Warning: failed to reopen MR: %v\n", err)
|
||||
_, _ = fmt.Fprintf(m.output, "Warning: failed to reopen MR: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,7 +486,7 @@ func (m *Manager) runTests(testCmd string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := exec.Command(parts[0], parts[1:]...)
|
||||
cmd := exec.Command(parts[0], parts[1:]...) //nolint:gosec // G204: testCmd is from trusted rig config
|
||||
cmd.Dir = m.workDir
|
||||
|
||||
var stderr bytes.Buffer
|
||||
@@ -571,7 +571,7 @@ func (m *Manager) pushWithRetry(targetBranch string, config MergeConfig) error {
|
||||
|
||||
for attempt := 0; attempt <= config.PushRetryCount; attempt++ {
|
||||
if attempt > 0 {
|
||||
fmt.Fprintf(m.output, "Push retry %d/%d after %v\n", attempt, config.PushRetryCount, delay)
|
||||
_, _ = fmt.Fprintf(m.output, "Push retry %d/%d after %v\n", attempt, config.PushRetryCount, delay)
|
||||
time.Sleep(delay)
|
||||
delay *= 2 // Exponential backoff
|
||||
}
|
||||
@@ -731,7 +731,7 @@ func (m *Manager) Retry(id string, processNow bool) error {
|
||||
// The Refinery agent handles merge processing.
|
||||
// It will pick up this MR in its next patrol cycle.
|
||||
if processNow {
|
||||
fmt.Fprintln(m.output, "Note: --now is deprecated. The Refinery agent will process this MR in its next patrol cycle.")
|
||||
_, _ = fmt.Fprintln(m.output, "Note: --now is deprecated. The Refinery agent will process this MR in its next patrol cycle.")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user