Skip to content

fix: preserve working_dir on retry restore#2111

Merged
yohamta0 merged 1 commit into
mainfrom
codex/fix-retry-working-dir-2110
May 7, 2026
Merged

fix: preserve working_dir on retry restore#2111
yohamta0 merged 1 commit into
mainfrom
codex/fix-retry-working-dir-2110

Conversation

@yohamta0

@yohamta0 yohamta0 commented May 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • persist the effective DAG-run working directory and each node working directory in run history
  • restore retry execution context from persisted run/node history so retry reproduces the original run, including step retry working_dir
  • backfill only missing working-dir snapshots for older histories that predate the new persisted fields
  • add focused unit coverage plus an internal integration regression for retry working_dir reuse
  • make Windows dirlock acquisition retry transient Access denied states seen during concurrent lock transitions

Root cause

Retry rebuilt the DAG from the stored YAML snapshot, but the explicit working-dir marker was runtime-only and older run histories did not persist the effective run/node working directories. On retry, a different or absent --default-working-dir could make runtime prefer the new attempt work directory instead of the directory used by the original run.

Step retry had the same reproduction issue at node level: retry planning re-bound nodes to the restored DAG definition, so it needed to prefer the persisted node working directory over both the restored DAG step dir and the node's embedded step snapshot.

A Windows CI failure exposed a separate flaky distributed lease test: concurrent lock contenders can see CreateFile .dagu_lock: Access is denied while another contender is creating/removing the lock directory. That is the same transient lock-conflict class as Windows sharing violations and now retries through the existing dirlock loop.

Testing

  • go test ./internal/core/spec -run 'TestLoadYAMLWithOpts_MarksConfiguredWorkingDirExplicit|TestLoadYAMLWithOpts_PreservesLegacyContract' -count=1
  • go test ./internal/cmd -run 'TestRestoreRetryExecutionContext|TestRetryCommand/StepRetryPreservesExplicitWorkingDir|TestRestoreDAGFromStatus' -count=1
  • go test ./internal/runtime ./internal/runtime/transform -run 'TestCreateRetryPlan_PrefersPersistedNodeWorkingDir|TestNodeFieldsRoundTrip|TestStatusBuilderWithOptions' -count=1
  • go test ./internal/intg -run TestStepRetryReusesOriginalWorkingDir -count=1
  • go test ./internal/service/slack -count=1
  • go test ./internal/cmn/dirlock ./internal/persis/filedistributed -run 'TestRetryableLockStateError_WindowsAccessDenied|TestDAGRunLeaseStore_ConcurrentTouchPreservesLatestHeartbeat' -count=1
  • go test ./internal/persis/filedistributed -run TestDAGRunLeaseStore_ConcurrentTouchPreservesLatestHeartbeat -count=50
  • git diff --check
  • make check

Closes #2110

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Marks DAGs whose working_dir was explicitly configured and propagates that flag through YAML loading and DAG restoration; applies persisted run WorkingDir on restore, backfills legacy retry WorkingDir, threads WorkingDir through persistence/status/runtime/plan/runner, and adds tests covering loader, restore, retry, transform, and integration scenarios.

Changes

Working Directory Preservation During Retry

Layer / File(s) Summary
Data Shape
internal/core/exec/...
Add optional WorkingDir to persisted exec.Node and exec.DAGRunStatus; add transform.WithWorkingDir status option.
Runtime State
internal/runtime/data.go, internal/runtime/agent/agent.go, internal/runtime/runner.go
Add NodeState.WorkingDir, Data.SetWorkingDir; Agent includes evaluated working dir in status/model; Runner applies node working-dir into PlanEnv early.
Transform / Round-trip
internal/runtime/transform/...
Include WorkingDir in persistence↔runtime conversions and extend tests to cover WorkingDir and chat-message round-trip.
Loader: explicitness marking
internal/core/spec/loader.go
Add markConfiguredWorkingDirsExplicit and refactor applyWorkingDirFallback so only configured WorkingDir values set WorkingDirExplicit; call from LoadYAMLWithOpts.
CLI helper: restore
internal/cmd/helper.go
Rebuild DAG via rebuildDAGFromYAML then apply persisted exec.DAGRunStatus.WorkingDir to the rebuilt DAG via applyPersistedRunWorkingDir.
Retry core
internal/cmd/retry.go
After restoring DAG for retry, call restoreRetryExecutionContext to backfill status.WorkingDir when appropriate and add helper to detect non-default stored DAG working dir.
Runtime plan & runner
internal/runtime/plan.go, internal/runtime/runner.go
Add retryStepForNode to prefer persisted node WorkingDir during retry rebinding; apply node WorkingDir into env early in Runner.setupVariables/setupEnvironEventHandler.
Unit / Integration Tests
internal/core/spec/loader_test.go, internal/cmd/helper_test.go, internal/cmd/retry_test.go, internal/intg/retry_test.go, internal/runtime/plan_test.go, internal/runtime/transform/status_test.go
Add tests asserting WorkingDirExplicit marking, restore preserves configured/persisted WorkingDir, retry reuses persisted WorkingDir, and status builder includes WorkingDir.
Service testability
internal/service/slack/bot.go, internal/service/slack/bot_test.go
Make incoming-after scheduling injectable for deterministic batching tests and update test to capture and invoke scheduled callbacks.
Scheduler tests
internal/intg/sched_test.go
Stabilize simulated clock and tighten polling timeout for cron schedule test.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • dagucloud/dagu#2080: Both PRs modify runtime environment setup for step execution in internal/runtime/runner.go (ensuring the env seen by preconditions/step execution is established earlier), so they are related.
  • dagucloud/dagu#2034: Both PRs modify internal/core/spec/loader.go’s working-dir handling (refactoring applyWorkingDirFallback and adding a recursive helper to mark WorkingDirExplicit), so the changes are directly related.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: preserve working_dir on retry restore' directly summarizes the main change: preserving working_dir when restoring DAGs during retry operations.
Linked Issues check ✅ Passed The PR addresses all objectives from issue #2110: preserves working_dir for retried steps, maintains provenance across retry rebuilds (DAG YAML, base config, default), and restores runtime metadata to prevent fallback to attempt work directory.
Out of Scope Changes check ✅ Passed All changes are directly related to preserving and restoring working_dir during retry operations; minor unrelated fixes (Slack bot delay scheduling, scheduler clock simulation) are necessary infrastructure improvements that do not distract from the main objective.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-retry-working-dir-2110

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch from c7a5ffa to 4737467 Compare May 7, 2026 03:21
@yohamta0

yohamta0 commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch from 4737467 to f556687 Compare May 7, 2026 03:28
@yohamta0

yohamta0 commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch from f556687 to e2edfc5 Compare May 7, 2026 03:44
@yohamta0

yohamta0 commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/core/spec/loader.go (1)

380-399: ⚡ Quick win

Stale doc comment — now describes only half of the function's behavior.

The comment on line 380 says "sets a working directory when the manifest omits one" but the function also marks explicitly-configured directories. Consider updating it.

📝 Suggested doc update
-// applyWorkingDirFallback sets a working directory when the manifest omits one.
+// applyWorkingDirFallback marks any already-configured working directory as
+// explicit and, when none is configured, synthesizes a fallback from filePath
+// or the process working directory.
 func applyWorkingDirFallback(dag *core.DAG, filePath string) error {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/core/spec/loader.go` around lines 380 - 399, Update the stale doc
comment for applyWorkingDirFallback to reflect both behaviors: it marks
explicitly-configured working directories (via
markConfiguredWorkingDirsExplicit) and then sets a default working directory
when none is provided on the DAG (using filePath or getDefaultWorkingDir()).
Mention both steps and the error-return case from getDefaultWorkingDir so the
comment accurately documents applyWorkingDirFallback's full behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/core/spec/loader.go`:
- Around line 380-399: Update the stale doc comment for applyWorkingDirFallback
to reflect both behaviors: it marks explicitly-configured working directories
(via markConfiguredWorkingDirsExplicit) and then sets a default working
directory when none is provided on the DAG (using filePath or
getDefaultWorkingDir()). Mention both steps and the error-return case from
getDefaultWorkingDir so the comment accurately documents
applyWorkingDirFallback's full behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a25caba3-1997-4afd-9d28-c6dfe8f1823b

📥 Commits

Reviewing files that changed from the base of the PR and between f556687 and e2edfc5.

📒 Files selected for processing (4)
  • internal/cmd/helper_test.go
  • internal/cmd/retry_test.go
  • internal/core/spec/loader.go
  • internal/core/spec/loader_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/core/spec/loader_test.go
  • internal/cmd/helper_test.go
  • internal/cmd/retry_test.go

@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch from e2edfc5 to 0d3d272 Compare May 7, 2026 04:00
@yohamta0

yohamta0 commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch from 0d3d272 to 1eb4a51 Compare May 7, 2026 04:01
@yohamta0

yohamta0 commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch from 1eb4a51 to 721f112 Compare May 7, 2026 04:43
@yohamta0

yohamta0 commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@yohamta0

yohamta0 commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai resume

@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch from 721f112 to 058423e Compare May 7, 2026 05:40
@yohamta0

yohamta0 commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch from 058423e to 6d9ee76 Compare May 7, 2026 05:58
@yohamta0

yohamta0 commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

2 similar comments
@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch from 6d9ee76 to feab602 Compare May 7, 2026 06:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/runtime/plan_test.go (1)

508-531: 💤 Low value

LGTM — the test correctly captures the core regression scenario: a persisted NodeState.WorkingDir must override the current DAG step's Dir when building the retry plan.

One minor coverage gap: the test only covers the case where node.Step.Dir is empty. Consider adding a sub-case where node.Step.Dir is also non-empty to document the explicit priority order.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/runtime/plan_test.go` around lines 508 - 531, Add a sub-case to
TestCreateRetryPlan_PrefersPersistedNodeWorkingDir that sets both the DAG
Step.Dir and the persisted node.Step.Dir to non-empty values and asserts that
runtime.CreateRetryPlan still prefers the persisted NodeState.WorkingDir (e.g.,
create node via runtime.NodeWithData with NodeData containing
Step:{Name:"target", Dir:"/node-specified"} and
NodeState.WorkingDir:"/original-work", call runtime.CreateRetryPlan, then assert
the returned plan's target.Step().Dir equals "/original-work"); this documents
the explicit priority order and uses the same helpers (NodeWithData, NodeData,
NodeState, core.Step) referenced in the existing test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/runtime/plan_test.go`:
- Around line 508-531: Add a sub-case to
TestCreateRetryPlan_PrefersPersistedNodeWorkingDir that sets both the DAG
Step.Dir and the persisted node.Step.Dir to non-empty values and asserts that
runtime.CreateRetryPlan still prefers the persisted NodeState.WorkingDir (e.g.,
create node via runtime.NodeWithData with NodeData containing
Step:{Name:"target", Dir:"/node-specified"} and
NodeState.WorkingDir:"/original-work", call runtime.CreateRetryPlan, then assert
the returned plan's target.Step().Dir equals "/original-work"); this documents
the explicit priority order and uses the same helpers (NodeWithData, NodeData,
NodeState, core.Step) referenced in the existing test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 75095cba-4708-414b-8a63-bee09527360c

📥 Commits

Reviewing files that changed from the base of the PR and between 1eb4a51 and 6d9ee76.

📒 Files selected for processing (20)
  • internal/cmd/helper.go
  • internal/cmd/helper_test.go
  • internal/cmd/retry.go
  • internal/cmd/retry_test.go
  • internal/core/exec/node.go
  • internal/core/exec/runstatus.go
  • internal/core/spec/loader.go
  • internal/core/spec/loader_test.go
  • internal/intg/retry_test.go
  • internal/runtime/agent/agent.go
  • internal/runtime/data.go
  • internal/runtime/plan.go
  • internal/runtime/plan_test.go
  • internal/runtime/runner.go
  • internal/runtime/transform/node.go
  • internal/runtime/transform/node_test.go
  • internal/runtime/transform/status.go
  • internal/runtime/transform/status_test.go
  • internal/service/slack/bot.go
  • internal/service/slack/bot_test.go
✅ Files skipped from review due to trivial changes (1)
  • internal/runtime/transform/node.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/core/spec/loader_test.go
  • internal/cmd/retry_test.go
  • internal/core/spec/loader.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/service/slack/bot.go (1)

111-121: 💤 Low value

Consider initialising incomingAfterFunc in New to avoid a per-call closure allocation.

In the production path every enqueueIncomingMessage call allocates a fresh func(time.Duration, func()) wrapper just to call time.AfterFunc. Setting the default once in New removes the nil-guard and the allocation entirely, while test code can still override the field directly.

♻️ Proposed refactor
 return &Bot{
     cfg:                   cfg,
     agentAPI:              agentAPI,
     slackClient:           slackClient,
     socketClient:          socketClient,
     allowedChannels:       allowed,
     eventService:          cfg.EventService,
     notificationStateFile: cfg.NotificationStateFile,
     logger:                logger,
     incomingDelay:         defaultIncomingBatchDelay,
+    incomingAfterFunc:     time.AfterFunc,
 }, nil
-    afterFunc := b.incomingAfterFunc
-    if afterFunc == nil {
-        afterFunc = func(delay time.Duration, fn func()) {
-            time.AfterFunc(delay, fn)
-        }
-    }
-    afterFunc(delay, func() {
+    b.incomingAfterFunc(delay, func() {
         b.flushIncomingMessages(ctx, cs, convKey, gen)
     })

Also applies to: 330-338

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/slack/bot.go` around lines 111 - 121, The Bot constructor
(New) should initialize the incomingAfterFunc field to time.AfterFunc (and
likewise initialize any similar timers mentioned around enqueueOutgoingMessage)
instead of leaving it nil so enqueueIncomingMessage doesn't allocate a new
closure on every call; update New to set b.incomingAfterFunc = time.AfterFunc,
remove the per-call nil-guard/closure allocation in enqueueIncomingMessage, and
allow tests to override the incomingAfterFunc field directly (same change
applies to the analogous outgoing timer field referenced in the review).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/service/slack/bot.go`:
- Around line 111-121: The Bot constructor (New) should initialize the
incomingAfterFunc field to time.AfterFunc (and likewise initialize any similar
timers mentioned around enqueueOutgoingMessage) instead of leaving it nil so
enqueueIncomingMessage doesn't allocate a new closure on every call; update New
to set b.incomingAfterFunc = time.AfterFunc, remove the per-call
nil-guard/closure allocation in enqueueIncomingMessage, and allow tests to
override the incomingAfterFunc field directly (same change applies to the
analogous outgoing timer field referenced in the review).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 80fda807-ff46-4b6f-b9b0-ecc992f4509f

📥 Commits

Reviewing files that changed from the base of the PR and between 6d9ee76 and feab602.

📒 Files selected for processing (21)
  • internal/cmd/helper.go
  • internal/cmd/helper_test.go
  • internal/cmd/retry.go
  • internal/cmd/retry_test.go
  • internal/core/exec/node.go
  • internal/core/exec/runstatus.go
  • internal/core/spec/loader.go
  • internal/core/spec/loader_test.go
  • internal/intg/retry_test.go
  • internal/intg/sched_test.go
  • internal/runtime/agent/agent.go
  • internal/runtime/data.go
  • internal/runtime/plan.go
  • internal/runtime/plan_test.go
  • internal/runtime/runner.go
  • internal/runtime/transform/node.go
  • internal/runtime/transform/node_test.go
  • internal/runtime/transform/status.go
  • internal/runtime/transform/status_test.go
  • internal/service/slack/bot.go
  • internal/service/slack/bot_test.go
✅ Files skipped from review due to trivial changes (2)
  • internal/runtime/transform/node.go
  • internal/cmd/retry_test.go
🚧 Files skipped from review as they are similar to previous changes (15)
  • internal/core/exec/runstatus.go
  • internal/runtime/data.go
  • internal/runtime/transform/status.go
  • internal/core/exec/node.go
  • internal/service/slack/bot_test.go
  • internal/runtime/agent/agent.go
  • internal/intg/sched_test.go
  • internal/runtime/plan_test.go
  • internal/runtime/plan.go
  • internal/cmd/retry.go
  • internal/runtime/transform/node_test.go
  • internal/cmd/helper_test.go
  • internal/core/spec/loader_test.go
  • internal/core/spec/loader.go
  • internal/intg/retry_test.go

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Reviews resumed.

@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch from feab602 to 626d81b Compare May 7, 2026 06:36
@yohamta0

yohamta0 commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

1 similar comment
@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/service/slack/bot.go (1)

111-122: 💤 Low value

Consider initialising incomingAfterFunc to its default in New() to avoid a per-call closure allocation.

The nil-check at line 331 creates a fresh wrapper closure on every production enqueueIncomingMessage call. Seeding the field in New() avoids the repeated allocation and removes the branch from the hot path entirely.

♻️ Proposed refactor
 return &Bot{
     cfg:                   cfg,
     agentAPI:              agentAPI,
     slackClient:           slackClient,
     socketClient:          socketClient,
     allowedChannels:       allowed,
     eventService:          cfg.EventService,
     notificationStateFile: cfg.NotificationStateFile,
     logger:                logger,
     incomingDelay:         defaultIncomingBatchDelay,
+    incomingAfterFunc: func(d time.Duration, fn func()) {
+        time.AfterFunc(d, fn)
+    },
 }, nil

Then simplify the dispatch site:

-afterFunc := b.incomingAfterFunc
-if afterFunc == nil {
-    afterFunc = func(delay time.Duration, fn func()) {
-        time.AfterFunc(delay, fn)
-    }
-}
-afterFunc(delay, func() {
+b.incomingAfterFunc(delay, func() {
     b.flushIncomingMessages(ctx, cs, convKey, gen)
 })

Tests override bot.incomingAfterFunc after construction as before; New() always produces a non-nil field, so the nil guard is no longer needed.

Also applies to: 330-338

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/slack/bot.go` around lines 111 - 122, The Bot constructor
(New) should initialize the incomingAfterFunc field to the default
implementation to avoid allocating a new closure on every enqueueIncomingMessage
call; update New() to set Bot.incomingAfterFunc to the same default timer/after
behavior used today so enqueueIncomingMessage no longer needs its nil-check and
per-call wrapper allocation, and keep tests able to override
bot.incomingAfterFunc as before; ensure you update references to
incomingAfterFunc, New, and enqueueIncomingMessage accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/service/slack/bot.go`:
- Around line 111-122: The Bot constructor (New) should initialize the
incomingAfterFunc field to the default implementation to avoid allocating a new
closure on every enqueueIncomingMessage call; update New() to set
Bot.incomingAfterFunc to the same default timer/after behavior used today so
enqueueIncomingMessage no longer needs its nil-check and per-call wrapper
allocation, and keep tests able to override bot.incomingAfterFunc as before;
ensure you update references to incomingAfterFunc, New, and
enqueueIncomingMessage accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8a1b0450-3ce3-4f22-bd97-3a288b6ddd4e

📥 Commits

Reviewing files that changed from the base of the PR and between feab602 and 626d81b.

📒 Files selected for processing (22)
  • internal/cmd/helper.go
  • internal/cmd/helper_test.go
  • internal/cmd/retry.go
  • internal/cmd/retry_internal_test.go
  • internal/cmd/retry_test.go
  • internal/core/exec/node.go
  • internal/core/exec/runstatus.go
  • internal/core/spec/loader.go
  • internal/core/spec/loader_test.go
  • internal/intg/retry_test.go
  • internal/intg/sched_test.go
  • internal/runtime/agent/agent.go
  • internal/runtime/data.go
  • internal/runtime/plan.go
  • internal/runtime/plan_test.go
  • internal/runtime/runner.go
  • internal/runtime/transform/node.go
  • internal/runtime/transform/node_test.go
  • internal/runtime/transform/status.go
  • internal/runtime/transform/status_test.go
  • internal/service/slack/bot.go
  • internal/service/slack/bot_test.go
✅ Files skipped from review due to trivial changes (1)
  • internal/runtime/runner.go
🚧 Files skipped from review as they are similar to previous changes (17)
  • internal/runtime/transform/status_test.go
  • internal/runtime/transform/node.go
  • internal/core/exec/node.go
  • internal/core/exec/runstatus.go
  • internal/runtime/data.go
  • internal/intg/sched_test.go
  • internal/runtime/agent/agent.go
  • internal/runtime/plan_test.go
  • internal/cmd/retry.go
  • internal/cmd/retry_test.go
  • internal/service/slack/bot_test.go
  • internal/core/spec/loader_test.go
  • internal/runtime/plan.go
  • internal/runtime/transform/node_test.go
  • internal/cmd/helper.go
  • internal/core/spec/loader.go
  • internal/cmd/helper_test.go

@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch 4 times, most recently from b25e423 to 164abd6 Compare May 7, 2026 08:33
@yohamta0 yohamta0 force-pushed the codex/fix-retry-working-dir-2110 branch from 164abd6 to b289ecb Compare May 7, 2026 08:49
@yohamta0 yohamta0 merged commit f5d784e into main May 7, 2026
10 checks passed
@yohamta0 yohamta0 deleted the codex/fix-retry-working-dir-2110 branch May 7, 2026 09:14
@coderabbitai coderabbitai Bot mentioned this pull request May 17, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: When I retry a step , the " working_dir" was lost and the command script can not found

1 participant