Skip to content

feat(runner): add collection runner for batch request execution ✨ - #78

Open
kbrdn1 wants to merge 6 commits into
mainfrom
feat/#44-collection-runner
Open

kbrdn1 wants to merge 6 commits into
mainfrom
feat/#44-collection-runner

Conversation

@kbrdn1

@kbrdn1 kbrdn1 commented Jan 18, 2026

Copy link
Copy Markdown
Owner

Description

Add a Collection Runner feature that enables batch execution of HTTP requests from collections or folders. This feature allows users to run multiple requests sequentially with configurable delays, stop-on-failure behavior, and comprehensive reporting.

Related Issue

Closes #44

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactoring (no functional changes)
  • Documentation update
  • Performance improvement
  • Test coverage improvement

Changes Made

Core API (internal/api/runner.go)

  • RunSession: Manages execution state with session-scoped environment cloning
  • RunConfig: Configurable parameters (delay, timeout, stop-on-failure, script timeout)
  • RequestResult: Captures individual request results with timing and script outputs
  • RunReport: Comprehensive summary with pass/fail/skip statistics
  • CollectFromCollection: Depth-first request collection from folders

UI Components (internal/ui/)

  • RunnerModal: Interactive modal with animated progress bar and results list
  • runner_messages.go: Bubble Tea messages for runner lifecycle events
  • runner_commands.go: Execution commands with pre/post-script support

Integration

  • Ctrl+R keybinding to launch runner from Collections panel
  • Modal overlay with keyboard controls (q/Escape to cancel, e to export)
  • Session-scoped environment isolation for variable modifications

Key Features

  • ✅ Sequential request execution with progress tracking
  • ✅ Pre-request and post-response script execution
  • ✅ Variable substitution AFTER pre-request scripts (critical for dynamic values)
  • ✅ Stop-on-failure with remaining requests marked as skipped
  • ✅ JSON report export to .lazycurl/reports/
  • ✅ Real-time progress display with animated loader

Testing

  • Manual testing performed
  • Unit tests added/updated (26 new tests, ~72% coverage)
  • All existing tests pass (make test)
  • Build succeeds (make build)

Test Coverage

ok  github.com/kbrdn1/LazyCurl/internal/api    0.216s
ok  github.com/kbrdn1/LazyCurl/internal/ui     0.020s

Screenshots (if applicable)

Runner modal shows progress bar, request list with status icons, and real-time updates

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes

Additional Notes

Critical Bug Fix

During multi-agent verification, a timing bug was identified and fixed: variable substitution was happening BEFORE pre-request script execution, which meant variables set by scripts weren't being substituted in the request URL. The fix reorders the execution flow:

  1. Pre-request script execution
  2. Apply environment changes from script
  3. Apply script modifications to request
  4. Variable substitution (now sees script-set variables)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Collection runner with modal UI to execute requests, show progress, and export run reports
    • Configurable run options (stop-on-failure, delays, timeouts) and pre/post-request script support
    • Keybinding (Ctrl+R) to start runs; UI exposes selected collection/folder context
  • Tests

    • Extensive test coverage for runner lifecycle, config validation, collection traversal, reporting, and helpers

✏️ Tip: You can customize this high-level summary in your review settings.

kbrdn1 and others added 5 commits January 18, 2026 04:17
Add core types for the collection runner feature:
- RunSession: manages execution state with session-scoped environment
- RunConfig: configurable execution parameters (delay, timeout, stop-on-failure)
- RequestResult: captures individual request execution results
- RunReport: comprehensive summary with statistics and timing
- Request collector for traversing collection folders

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add unit tests covering:
- RunStatus string conversion
- RunConfig validation (delay limits, timeout validation)
- RequestResult state transitions
- RunSession lifecycle (start, stop, cancel, complete)
- Progress tracking and percentage calculation
- RunReport generation with statistics
- Export functionality with directory creation

Coverage: ~72% for runner package

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add UI components for collection runner:
- RunnerModal: interactive modal with progress bar and results list
- Bubble Tea messages for runner lifecycle events
- Animated loader with spinner states
- Keyboard controls (q/Escape to cancel, e to export)
- Real-time progress tracking with visual feedback

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Bubble Tea commands for runner execution:
- ExecuteCollectionRunCmd: initializes and validates run session
- ExecuteNextRequestCmd: executes requests with script support
- DelayCmd: handles inter-request delays
- CancelRunCmd: graceful cancellation with cleanup
- ExportRunReportCmd: exports results to JSON

Key features:
- Pre-request script execution before variable substitution
- Post-response script with environment updates
- Stop-on-failure support with remaining requests marked as skipped
- Session-scoped environment isolation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Integrate runner with main model:
- Add Ctrl+R keybinding to launch runner from collections panel
- Add GetSelectedCollection/GetSelectedFolderPath helpers
- Wire all runner message handlers in Update()
- Add runner modal overlay in View()
- Handle input priority for modal focus

Supports running entire collections or specific folders with
real-time progress display and cancellation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings January 18, 2026 03:20
@coderabbitai

coderabbitai Bot commented Jan 18, 2026

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds a collection runner: core runner model and types, session lifecycle and reporting (including export), collection traversal, extensive tests, new UI modal and command/message plumbing, keybinding, and integration of pre/post script execution into per-request run flow.

Changes

Cohort / File(s) Summary
Core Runner Model
internal/api/runner.go
New runner types and enums (RunStatus, ResultStatus), RunConfig + validation, RequestResult lifecycle, RunSession lifecycle, report types (SessionInfo, SummaryStats, RunReport), collection traversal (CollectRequests / CollectFromCollection), export (ExportRunReport), and helpers (generateRunID, sanitizeForID, flattenHeaders).
Runner Tests
internal/api/runner_test.go
Comprehensive tests covering enums, RunConfig validation, RequestResult methods, RunSession lifecycle, collection utilities, GenerateReport and ExportRunReport, helpers, and integration scenarios.
UI: Keybinding & Selection
internal/config/config.go, internal/ui/collections_view.go
Adds RunCollection keybinding (ctrl+r) and two accessors on CollectionsView: GetSelectedCollection() and GetSelectedFolderPath().
UI: Model Integration
internal/ui/model.go
Integrates runner state and message handling into the main UI model (modal overlay, runner lifecycle messages, tick handling).
UI: Commands / Orchestration
internal/ui/runner_commands.go
Commands to execute collection runs, advance requests, delay, cancel, export reports; implements pre/post script execution, env propagation, substitution, HTTP dispatch, stop-on-failure and skipping remaining requests.
UI: Messages
internal/ui/runner_messages.go
New exported runner message types (start, started, progress, request complete, complete, cancel, cancelled, export, exported, show/hide modal, tick, execute next, delay complete, error).
UI: Runner Modal
internal/ui/runner_modal.go
New RunnerModal component with rendering, scrolling, key handling, loader tick, export status, and session updates (show/hide, SetExported, UpdateSession).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI_Model
    participant Runner_Cmd
    participant API_Runner
    participant HTTP_Client
    participant Modal

    User->>UI_Model: trigger run (ctrl+r)
    UI_Model->>Runner_Cmd: ExecuteCollectionRunCmd(collection, folderPath, env, cfg)
    Runner_Cmd->>API_Runner: CollectRequests(...)
    API_Runner-->>Runner_Cmd: requests[]
    Runner_Cmd->>API_Runner: NewRunSession(...)
    API_Runner-->>Runner_Cmd: session
    Runner_Cmd->>UI_Model: RunnerStartedMsg(session, requests)
    UI_Model->>Modal: Show(session, requests)

    loop per request
        Runner_Cmd->>Runner_Cmd: run pre-script -> apply env changes
        Runner_Cmd->>HTTP_Client: send request
        HTTP_Client-->>Runner_Cmd: response
        Runner_Cmd->>Runner_Cmd: run post-script -> apply env changes
        Runner_Cmd->>API_Runner: AddResult(...)
        Runner_Cmd->>UI_Model: RunnerRequestCompleteMsg(result, session)
        Runner_Cmd->>Runner_Cmd: maybe delay / check stop-on-failure
    end

    Runner_Cmd->>API_Runner: GenerateReport(session)
    API_Runner-->>Runner_Cmd: report
    Runner_Cmd->>UI_Model: RunnerCompleteMsg(session, report)
    UI_Model->>Modal: UpdateSession() / SetExported(...)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

feature, ui/ux, test

Poem

🐰 I hop through folders, one request at a time,
I sniff pre-scripts and nibble on each line,
I chase responses, tally pass and fail,
Then gift a JSON report upon the trail —
Hooray! A runner's hop, neat, fast, and fine.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.23% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main feature addition: a collection runner for batch request execution.
Linked Issues check ✅ Passed The PR implements all coding requirements from issue #44: sequential collection/folder execution, progress tracking, pass/fail reporting, configurable delays, stop-on-failure option, and JSON report export.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the collection runner feature: runner model (runner.go, runner_test.go), UI components (runner_modal.go, runner_commands.go, runner_messages.go), UI integration (model.go, collections_view.go), and keybinding configuration.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request implements a comprehensive Collection Runner feature that enables batch execution of HTTP requests from collections or folders. The runner supports sequential execution with configurable delays, stop-on-failure behavior, pre/post-request script execution, and detailed reporting with JSON export capabilities.

Changes:

  • Added core runner API with session management, request result tracking, and comprehensive reporting
  • Implemented interactive UI modal with real-time progress tracking, animated loader, and scrollable results
  • Integrated runner into the main application with Ctrl+R keybinding and full message flow handling

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
internal/api/runner.go Core runner logic with session management, request result types, report generation, and JSON export
internal/api/runner_test.go Comprehensive test suite with 26 tests covering configuration, execution flow, and report generation
internal/ui/runner_modal.go Interactive modal component with progress bar, status indicators, scrolling, and keyboard controls
internal/ui/runner_messages.go Message type definitions for runner lifecycle events and communication
internal/ui/runner_commands.go Command functions for request execution, script handling, and runner control flow
internal/ui/model.go Integration of runner into main model with message handlers and state management
internal/ui/collections_view.go Helper methods to retrieve selected collection and folder path for runner initialization
internal/config/config.go Added run_collection keybinding configuration (Ctrl+R)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +106 to +118
switch msg.String() {
case "esc":
if m.session.IsTerminal() {
// Close modal when run is finished
m.Hide()
return m, func() tea.Msg {
return RunnerHideModalMsg{}
}
}
// Cancel running execution
return m, func() tea.Msg {
return RunnerCancelMsg{}
}

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

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

The PR description mentions "q/Escape to cancel" as a keyboard control, but the code only handles "esc" key, not "q". The key handler in the Update method does not include a case for "q". This creates a discrepancy between documentation and implementation.

Copilot uses AI. Check for mistakes.
Comment thread internal/ui/model.go
Comment on lines +3113 to +3118
m.statusBar.Success("Run complete",
fmt.Sprintf("%d/%d passed, %d failed, %d errors",
report.Summary.PassedAssertions,
report.Summary.TotalAssertions,
report.Summary.FailedAssertions,
report.Summary.Errors))

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

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

The status bar message format may be confusing. It displays "passed/total passed, failed, errors" where "passed/total" uses TotalAssertions as the denominator, but TotalAssertions represents the total number of assertions run (passed + failed), not the total number of requests. This could be misleading. Consider reformatting to clarify whether these numbers represent assertions or requests, e.g., "Assertions: X/Y passed" or "Requests: X passed, Y failed, Z errors".

Copilot uses AI. Check for mistakes.

@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.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@internal/api/runner_test.go`:
- Around line 823-853: The test TestExportRunReport_DirectoryPermissions calls
os.Getuid(), which is not available on Windows; update the test to guard that
root-skip logic with a platform check (import runtime) so Windows builds don't
call os.Getuid(): change the root check to something like "if runtime.GOOS !=
\"windows\" && os.Getuid() == 0 { t.Skip(...) }" (ensure runtime is imported) so
the test compiles and still skips when running as root on Unix; keep the rest of
the TestExportRunReport_DirectoryPermissions logic unchanged.

In `@internal/api/runner.go`:
- Around line 446-468: ExportRunReport uses second-precision timestamps so
concurrent calls can produce filename collisions; update ExportRunReport to
generate higher-resolution unique filenames (e.g., include nanoseconds or a UUID
suffix) by changing the timestamp format in the filename generation (function
ExportRunReport and the filename variable) and ensure the filepath.Join logic
remains the same; additionally add a unit test that concurrently calls
ExportRunReport (multiple goroutines) and asserts all returned paths are unique
and files exist to verify collision prevention.

In `@internal/ui/runner_commands.go`:
- Around line 155-181: SetCompleted currently records the HTTP-only timing but
later the code overwrites result.Duration and result.DurationMs with
time.Since(startTime) after running the post-response script
(ExecutePostResponse / applyEnvChanges), causing the HTTP duration to be lost;
preserve the HTTP-only duration by removing the second overwrite or by storing
total duration in a separate field (e.g., TotalDuration/TotalDurationMs) and
keeping result.Duration/result.DurationMs as the HTTP-only values set by
SetCompleted, and when updating script results ensure you call
applyEnvChanges(session, postScriptResult) without resetting
result.Duration/result.DurationMs.

In `@internal/ui/runner_modal.go`:
- Around line 188-196: The visibleResultRows calculation currently subtracts a
fixed offset from m.height which can overestimate space when the modal is
capped; change visibleResultRows in RunnerModal to compute available rows from
the actual modal height (the effective/capped modal size) rather than the raw
terminal height. Use the modal's effective height (e.g., an existing field or
helper like modalHeight, effectiveHeight, or min(m.height, m.maxModalHeight)) to
subtract the header/progress/summary/footer/borders (the same 16 offset) and
keep the minimum floor (3) as before; update visibleResultRows to reference that
effective modal height so scrolling is accurate when the modal is capped.
- Around line 276-314: renderProgress can divide by zero because
m.session.TotalRequests may be 0 before Start(); update renderProgress to guard
the percent calculation the same way renderResults does: check if
m.session.TotalRequests == 0 and set percent to 0.0 (or otherwise avoid
division) before computing filled/empty from barWidth, using
m.session.CurrentIndex and m.session.TotalRequests to locate the values; ensure
the computed filled value is clamped between 0 and barWidth to avoid negative or
overflow values when constructing the bar.
🧹 Nitpick comments (3)
internal/api/runner.go (1)

475-480: Run IDs can collide within the same second.
Using second precision can duplicate IDs for rapid successive runs; consider higher resolution.

🔧 Proposed tweak
-	return fmt.Sprintf("run_%d_%s", time.Now().Unix(), sanitized)
+	return fmt.Sprintf("run_%d_%s", time.Now().UnixNano(), sanitized)
internal/api/runner_test.go (1)

113-216: Convert single‑case tests to table‑driven style.
Several tests here are single-case (e.g., DefaultRunConfig, NewRequestResult, SetRunning/SetCompleted/SetError). Refactor to struct‑slice tables for consistency. As per coding guidelines, please use table‑driven tests.

internal/ui/runner_commands.go (1)

350-357: String-based timeout detection is fragile but acceptable.

The isTimeoutError function relies on string matching in error messages. This works for standard Go HTTP timeouts but could miss wrapped errors or fail with localized error messages.

Consider using errors.As with net.Error interface for more robust timeout detection if this becomes problematic:

var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
    return true
}

Comment thread internal/api/runner_test.go
Comment thread internal/api/runner.go
Comment on lines +446 to +468
// ExportRunReport saves the report to .lazycurl/reports/
func ExportRunReport(report *RunReport) (string, error) {
// Ensure directory exists
dir := ".lazycurl/reports"
if err := os.MkdirAll(dir, 0755); err != nil {
return "", fmt.Errorf("failed to create reports directory: %w", err)
}

// Generate filename with timestamp
filename := fmt.Sprintf("run_%s.json", time.Now().Format("20060102_150405"))
path := filepath.Join(dir, filename)

// Marshal and write
data, err := json.MarshalIndent(report, "", " ")
if err != nil {
return "", fmt.Errorf("failed to marshal report: %w", err)
}

if err := os.WriteFile(path, data, 0644); err != nil {
return "", fmt.Errorf("failed to write report file: %w", err)
}

return path, nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check if the file follows snake_case naming convention
ls -la internal/api/ | grep -E "\.go$"

Repository: kbrdn1/LazyCurl

Length of output: 3539


🏁 Script executed:

# Search for ExportRunReport usage to understand if concurrent calls are possible
rg "ExportRunReport" --type go -A 3 -B 3

Repository: kbrdn1/LazyCurl

Length of output: 3067


🏁 Script executed:

# Check for any existing tests related to ExportRunReport
fd "test" internal/api/ --type f -name "*.go" | xargs rg "ExportRunReport" 2>/dev/null || echo "No tests found for ExportRunReport"

Repository: kbrdn1/LazyCurl

Length of output: 1219


🏁 Script executed:

# Check the full context of the ExportRunReport function
sed -n '446,468p' internal/api/runner.go

Repository: kbrdn1/LazyCurl

Length of output: 804


🏁 Script executed:

# Check if there are multiple calls to ExportRunReport or concurrent patterns
rg -B 5 -A 5 "ExportRunReport" --type go | head -50

Repository: kbrdn1/LazyCurl

Length of output: 2681


🏁 Script executed:

# Check the full test file for ExportRunReport tests
grep -A 30 "func TestExportRunReport" internal/api/runner_test.go

Repository: kbrdn1/LazyCurl

Length of output: 1886


🏁 Script executed:

# Check if there are any tests for concurrent/collision scenarios
rg "TestExportRunReport|concurrent|collision|parallel" internal/api/runner_test.go

Repository: kbrdn1/LazyCurl

Length of output: 161


Add higher-resolution filename suffix to prevent collision on concurrent exports.

The current timestamp format uses second precision, so multiple concurrent exports within the same second will overwrite each other. The Tea framework executes commands asynchronously, making concurrent calls possible.

🔧 Proposed fix
-	// Generate filename with timestamp
-	filename := fmt.Sprintf("run_%s.json", time.Now().Format("20060102_150405"))
+	// Generate filename with high-resolution timestamp
+	ts := time.Now()
+	filename := fmt.Sprintf("run_%s_%09d.json", ts.Format("20060102_150405"), ts.Nanosecond())

Additionally, add a test case for concurrent exports to verify collision prevention, as this is a public API function.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// ExportRunReport saves the report to .lazycurl/reports/
func ExportRunReport(report *RunReport) (string, error) {
// Ensure directory exists
dir := ".lazycurl/reports"
if err := os.MkdirAll(dir, 0755); err != nil {
return "", fmt.Errorf("failed to create reports directory: %w", err)
}
// Generate filename with timestamp
filename := fmt.Sprintf("run_%s.json", time.Now().Format("20060102_150405"))
path := filepath.Join(dir, filename)
// Marshal and write
data, err := json.MarshalIndent(report, "", " ")
if err != nil {
return "", fmt.Errorf("failed to marshal report: %w", err)
}
if err := os.WriteFile(path, data, 0644); err != nil {
return "", fmt.Errorf("failed to write report file: %w", err)
}
return path, nil
// ExportRunReport saves the report to .lazycurl/reports/
func ExportRunReport(report *RunReport) (string, error) {
// Ensure directory exists
dir := ".lazycurl/reports"
if err := os.MkdirAll(dir, 0755); err != nil {
return "", fmt.Errorf("failed to create reports directory: %w", err)
}
// Generate filename with high-resolution timestamp
ts := time.Now()
filename := fmt.Sprintf("run_%s_%09d.json", ts.Format("20060102_150405"), ts.Nanosecond())
path := filepath.Join(dir, filename)
// Marshal and write
data, err := json.MarshalIndent(report, "", " ")
if err != nil {
return "", fmt.Errorf("failed to marshal report: %w", err)
}
if err := os.WriteFile(path, data, 0644); err != nil {
return "", fmt.Errorf("failed to write report file: %w", err)
}
return path, nil
🤖 Prompt for AI Agents
In `@internal/api/runner.go` around lines 446 - 468, ExportRunReport uses
second-precision timestamps so concurrent calls can produce filename collisions;
update ExportRunReport to generate higher-resolution unique filenames (e.g.,
include nanoseconds or a UUID suffix) by changing the timestamp format in the
filename generation (function ExportRunReport and the filename variable) and
ensure the filepath.Join logic remains the same; additionally add a unit test
that concurrently calls ExportRunReport (multiple goroutines) and asserts all
returned paths are unique and files exist to verify collision prevention.

Comment on lines +155 to +181
// Set completed with response
result.SetCompleted(resp, time.Since(startTime))

// Execute post-response script if present
var postScriptResult *api.ScriptResult
postScript := getPostResponseScript(collReq)
if postScript != "" {
scriptReq := api.NewScriptRequestFromHTTP(httpReq)
scriptResp := createScriptResponseFromHTTP(resp)
env := sessionEnvToEnvironment(session.SessionEnv)

var err error
postScriptResult, err = executor.ExecutePostResponse(postScript, scriptReq, scriptResp, env)
if err != nil {
result.SetError("script", "Post-response script error", err.Error())
} else {
// Apply environment changes from post-response script
applyEnvChanges(session, postScriptResult)
}
}

// Set script results
result.SetScriptResults(preScriptResult, postScriptResult)

// Finalize timing
result.Duration = time.Since(startTime)
result.DurationMs = result.Duration.Milliseconds()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

ast-grep --pattern $'func ($_) SetCompleted($_, $_) {
  $$$
}'

Repository: kbrdn1/LazyCurl

Length of output: 805


Duration fields are overwritten after post-response script execution.

SetCompleted sets result.Duration and result.DurationMs on line 156 with the elapsed time to the HTTP response. However, lines 180-181 overwrite these values with time.Since(startTime) executed after the post-response script (lines 158-174) completes.

This means the final duration includes both HTTP request time and post-response script execution time. Verify if this is intentional—you may want separate metrics for HTTP-only duration (performance analysis) and total duration (including scripts).

🤖 Prompt for AI Agents
In `@internal/ui/runner_commands.go` around lines 155 - 181, SetCompleted
currently records the HTTP-only timing but later the code overwrites
result.Duration and result.DurationMs with time.Since(startTime) after running
the post-response script (ExecutePostResponse / applyEnvChanges), causing the
HTTP duration to be lost; preserve the HTTP-only duration by removing the second
overwrite or by storing total duration in a separate field (e.g.,
TotalDuration/TotalDurationMs) and keeping result.Duration/result.DurationMs as
the HTTP-only values set by SetCompleted, and when updating script results
ensure you call applyEnvChanges(session, postScriptResult) without resetting
result.Duration/result.DurationMs.

Comment thread internal/ui/runner_modal.go
Comment thread internal/ui/runner_modal.go
- Add Windows platform guard for os.Getuid() in tests
- Add nanosecond precision to report filenames to prevent collision
- Fix divide by zero guard in renderProgress
- Fix visibleResultRows calculation using effective modal height
- Add 'q' key handler to close modal when run is finished

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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.

Collection Runner

2 participants