Skip to content

feat(task): stream keep-order output in real-time per task#8164

Merged
jdx merged 4 commits intomainfrom
feat/keep-order-streaming
Feb 16, 2026
Merged

feat(task): stream keep-order output in real-time per task#8164
jdx merged 4 commits intomainfrom
feat/keep-order-streaming

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 15, 2026

Summary

  • keep-order mode now streams the active task's output in real-time instead of buffering everything until all tasks complete
  • One task at a time is "active" and streams live; other tasks buffer. When the active task finishes, finished tasks' buffers are flushed, then the next running task is promoted to stream live
  • Metadata messages ($ command echo, Finished in timing) are also routed through the streaming state so they stay properly ordered with task output

Closes #8136

Test plan

  • All 469 unit tests pass
  • test_task_keep_order e2e test passes
  • test_task_sequence_keep_order e2e test passes
  • All lint checks pass
  • Manual testing with parallel tasks confirms streaming behavior

🤖 Generated with Claude Code


Note

Medium Risk
Touches core concurrent task output routing and ordering, so regressions could manifest as missing/reordered output or deadlocks under parallel runs.

Overview
keep-order task output is reworked to stream one “active” task in real time while buffering other tasks and flushing buffers in definition order as tasks finish, instead of buffering everything until the end.

This introduces a new KeepOrderState in task_output_handler.rs, routes stdout/stderr and metadata (eprint) through it, and notifies it on task completion from the scheduler; final result display now only does a safety-net flush_all. Documentation for task_output is updated, and the test_task_keep_order e2e test adds a parallel slow/fast case to assert ordering is preserved even when completion order differs.

Written by Cursor Bugbot for commit 8e22c89. This will update automatically on new commits. Configure here.

Copilot AI review requested due to automatic review settings February 15, 2026 13:47
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the user experience of the mise run command's keep-order output mode. Instead of waiting for all tasks to complete before displaying any output, the system now provides real-time feedback for the currently active task. This change makes long-running parallel tasks feel more responsive and interactive, while still preserving the sequential order of task output as originally intended by the keep-order mode.

Highlights

  • Real-time Streaming for keep-order Mode: The keep-order output mode for mise run now streams the active task's output in real-time, rather than buffering all output until task completion.
  • Active Task Management: Only one task is 'active' and streams live output at a time. Other tasks buffer their output, which is then flushed when the active task finishes and the next task is promoted.
  • Ordered Metadata Messages: Metadata messages, such as command echoes and timing information, are now routed through the streaming state to ensure they remain properly ordered with the task's output.
Changelog
  • settings.toml
    • Updated the description for the keep-order output mode to reflect the new streaming behavior.
  • src/cli/run.rs
    • Integrated a call to the new on_task_finished method of the output handler when a task completes.
  • src/task/task_executor.rs
    • Modified the stdout and stderr handling for KeepOrder mode to delegate to the newly introduced KeepOrderState for real-time streaming and buffering.
  • src/task/task_output_handler.rs
    • Introduced a new KeepOrderLine enum to tag output lines by stream (stdout/stderr).
    • Implemented the KeepOrderState struct to manage the logic for active task streaming, buffering output from non-active tasks, and tracking finished tasks.
    • Added methods to KeepOrderState for initializing tasks, handling stdout and stderr lines, processing task completion, flushing buffered output, and promoting the next active task.
    • Replaced the keep_order_output field in OutputHandler with the new keep_order_state.
    • Updated the clone and new implementations of OutputHandler to correctly initialize and manage the KeepOrderState.
    • Modified init_task to use KeepOrderState::init_task for task initialization.
    • Updated the eprint method to route metadata messages through KeepOrderState::on_stderr when in KeepOrder mode, ensuring proper ordering.
  • src/task/task_results_display.rs
    • Updated the display_keep_order_output function to utilize the flush_all method of the KeepOrderState for a final flush of any remaining buffered output.
Activity
  • All 469 unit tests have passed.
  • The test_task_keep_order end-to-end test has passed.
  • The test_task_sequence_keep_order end-to-end test has passed.
  • All lint checks have passed.
  • Manual testing with parallel tasks is pending to confirm the new streaming behavior.
  • The pull request was generated using Claude Code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a great user experience improvement by streaming keep-order output in real-time. The implementation, centered around the new KeepOrderState, is well-structured. My review focuses on a few areas to enhance correctness, performance, and user-facing documentation:

  • The description for keep-order in settings.toml is no longer accurate with the new streaming behavior.
  • There's an opportunity to optimize performance in task_executor.rs by avoiding repeated string cloning for task prefixes.
  • The use of swap_remove in task_output_handler.rs could lead to a non-deterministic order for promoting tasks, which could be improved for predictability.

settings.toml Outdated
{ value = "prefix", description = "(default if jobs > 1) print by line with the prefix of the task name" },
{ value = "interleave", description = "(default if jobs == 1 or all tasks run sequentially) print output as it comes in" },
{ value = "keep-order", description = "print output from tasks in the order they are defined" },
{ value = "keep-order", description = "buffer output per-task and print each task's output when it finishes" },
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The updated description for keep-order appears to describe the old behavior of buffering all output until tasks complete. The new implementation streams one task live while buffering others, which is a significant improvement not reflected here. Updating the description will help users understand the new, improved behavior.

  { value = "keep-order", description = "Stream one task's output live while buffering others, printing all output in order as tasks complete" },

Comment on lines +672 to 680
let state = self.output_handler.keep_order_state.clone();
let task_clone = task.clone();
let prefix_str = prefix.to_string();
cmd = cmd.with_on_stdout(move |line| {
state
.lock()
.unwrap()
.on_stdout(&task_clone, prefix_str.clone(), line);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

In the with_on_stdout closure, prefix_str.clone() is called for every line of output. For tasks that produce a lot of output, this can be inefficient. A similar issue exists for with_on_stderr.

You could optimize this by using an Arc<String> for the prefix. This would involve a few changes across files:

  1. In src/task/task_executor.rs, create an Arc<String> for the prefix outside the closure and clone the Arc inside:

    let prefix_arc = std::sync::Arc::new(prefix.to_string());
    cmd = cmd.with_on_stdout(move |line| {
        state
            .lock()
            .unwrap()
            .on_stdout(&task_clone, prefix_arc.clone(), line);
    });
  2. In src/task/task_output_handler.rs, update KeepOrderLine to store an Arc<String>:

    pub enum KeepOrderLine {
        Stdout(std::sync::Arc<String>, String), // (prefix, line)
        Stderr(std::sync::Arc<String>, String), // (prefix, line)
    }
  3. Update on_stdout and on_stderr in KeepOrderState to accept Arc<String>:

    pub fn on_stdout(&mut self, task: &Task, prefix: std::sync::Arc<String>, line: String) {
        // ...
        self.buffers
            .entry(task.clone())
            .or_default()
            .push(KeepOrderLine::Stdout(prefix, line));
    }

This would replace expensive string cloning with cheap Arc cloning.

Comment on lines +76 to +89
self.buffers.swap_remove(task);
self.flush_finished();
self.promote_next();
} else {
// Non-active task finished — remember it for later flushing
self.finished.push(task.clone());
}
}

/// Flush all buffered output for tasks that have already finished.
fn flush_finished(&mut self) {
let finished = std::mem::take(&mut self.finished);
for task in finished {
if let Some(lines) = self.buffers.swap_remove(&task) {
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The use of swap_remove on self.buffers in on_task_finished (line 76) and flush_finished (line 89) can lead to unpredictable task promotion order. IndexMap::swap_remove is O(1) but reorders the remaining elements. Since promote_next picks self.buffers.first(), the next task to become active will not be deterministic based on the order they were started.

The comment for buffers says "insertion order preserved". To respect this and ensure predictable behavior, consider using shift_remove instead. It's O(n) but maintains the order of the remaining tasks. Given the number of parallel tasks is typically small, the performance impact should be acceptable.

You would need to change swap_remove to shift_remove on both lines 76 and 89.

@jdx jdx changed the title feat(run): stream keep-order output in real-time per task feat(task): stream keep-order output in real-time per task Feb 15, 2026
@jdx jdx force-pushed the feat/keep-order-streaming branch from f6bebfa to 7b9b083 Compare February 15, 2026 13:50
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates mise run keep-order output handling to support real-time streaming for one “active” task while buffering others, and to route metadata messages through the same ordering mechanism.

Changes:

  • Replaces the old per-task buffered stdout/stderr map with a KeepOrderState streaming state machine.
  • Routes keep-order stdout/stderr callbacks and metadata (eprint) through KeepOrderState.
  • Flushes/promotes keep-order state on task completion and at final results display.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/task/task_results_display.rs Switches final keep-order display to KeepOrderState::flush_all() safety-net flush.
src/task/task_output_handler.rs Introduces KeepOrderState and updates OutputHandler to use it; routes keep-order metadata through state.
src/task/task_executor.rs Updates keep-order stdout/stderr callbacks to stream/buffer via KeepOrderState.
src/cli/run.rs Notifies keep-order state when a task finishes so buffers can flush/promote.
settings.toml Updates the user-facing description string for task_output=keep-order.

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

this.add_failed_task(task.clone(), status);
}
if let Some(oh) = &this.output_handler {
oh.keep_order_state.lock().unwrap().on_task_finished(&task);
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

on_task_finished is called unconditionally for every task, even when the selected output mode is not keep-order. Since init_task only populates keep_order_state in keep-order mode, this path will just grow the finished list and retain task clones unnecessarily. Gate this call behind oh.output(None) == TaskOutput::KeepOrder (or equivalent) so state is only updated when needed.

Suggested change
oh.keep_order_state.lock().unwrap().on_task_finished(&task);
if oh.output(None) == crate::output::TaskOutput::KeepOrder {
oh.keep_order_state
.lock()
.unwrap()
.on_task_finished(&task);
}

Copilot uses AI. Check for mistakes.
settings.toml Outdated
{ value = "prefix", description = "(default if jobs > 1) print by line with the prefix of the task name" },
{ value = "interleave", description = "(default if jobs == 1 or all tasks run sequentially) print output as it comes in" },
{ value = "keep-order", description = "print output from tasks in the order they are defined" },
{ value = "keep-order", description = "stream one task at a time, buffering others to avoid interleaving" },
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The updated keep-order description no longer matches the behavior described in this PR (stream one task live while buffering others) and appears to conflict with existing CLI docs that say keep-order “keep[s] the order of the output”. Please update this description to reflect the new streaming semantics while clarifying what “order” means (definition order vs completion order).

Suggested change
{ value = "keep-order", description = "stream one task at a time, buffering others to avoid interleaving" },
{ value = "keep-order", description = "preserve task definition order while streaming: show one task's output live, buffering later tasks and replaying them after earlier ones finish so lines never interleave" },

Copilot uses AI. Check for mistakes.
Comment on lines +98 to +104
if let Some((task, _)) = self.buffers.first() {
let task = task.clone();
self.active = Some(task.clone());
if let Some(lines) = self.buffers.get_mut(&task) {
let lines = std::mem::take(lines);
Self::print_lines(&lines);
}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

promote_next promotes buffers.first() unconditionally, even if that task has an empty buffer (never produced output) or may not be running yet. This contradicts the comment about promoting the next running task with buffered output and can result in all other tasks being forced to buffer until the promoted task eventually emits output. Consider selecting the first task with a non-empty buffer (or tracking started/running tasks) and leaving active as None otherwise.

Suggested change
if let Some((task, _)) = self.buffers.first() {
let task = task.clone();
self.active = Some(task.clone());
if let Some(lines) = self.buffers.get_mut(&task) {
let lines = std::mem::take(lines);
Self::print_lines(&lines);
}
// Find the first task that actually has buffered output.
if let Some((task, lines)) = self
.buffers
.iter_mut()
.find(|(_, lines)| !lines.is_empty())
{
let task = task.clone();
self.active = Some(task.clone());
let lines = std::mem::take(lines);
Self::print_lines(&lines);

Copilot uses AI. Check for mistakes.
Comment on lines +73 to +79
if self.active.as_ref() == Some(task) {
// Active task finished — clear it, flush waiting tasks, promote next
self.active = None;
self.buffers.swap_remove(task);
self.flush_finished();
self.promote_next();
} else {
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

swap_remove on an IndexMap does not preserve insertion order, which can break keep-order semantics when you later use buffers.first() to pick the next task. Use an order-preserving removal method (e.g., shift_remove) or otherwise ensure the remaining task order is stable.

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +92
fn flush_finished(&mut self) {
let finished = std::mem::take(&mut self.finished);
for task in finished {
if let Some(lines) = self.buffers.swap_remove(&task) {
Self::print_lines(&lines);
}
}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

flush_finished removes buffers with swap_remove, which reorders the remaining IndexMap entries and can cause later promotion/printing to happen in the wrong task order. Prefer an order-preserving removal (shift_remove) or a different data structure that keeps the original task order intact.

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +54
pub fn on_stdout(&mut self, task: &Task, prefix: String, line: String) {
if self.active.is_none() || self.active.as_ref() == Some(task) {
self.active = Some(task.clone());
print_stdout(&prefix, &line);
} else {
self.buffers
.entry(task.clone())
.or_default()
.push(KeepOrderLine::Stdout(prefix, line));
}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

When active is None, the first task to emit output becomes active and prints immediately. This can allow a later-defined task to print before earlier tasks, violating the stated keep-order behavior. Consider selecting the initial active task based on the pre-initialized task order (from init_task) and buffering output until that task is active, or otherwise ensure output cannot appear before earlier tasks in the configured order.

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +93
/// Flush all buffered output for tasks that have already finished.
fn flush_finished(&mut self) {
let finished = std::mem::take(&mut self.finished);
for task in finished {
if let Some(lines) = self.buffers.swap_remove(&task) {
Self::print_lines(&lines);
}
}
}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

finished is tracked in completion order and then flushed in that same order. If multiple non-active tasks finish while another task is active, this can print buffers out of the original keep-order sequence (e.g., task C finishes before task B). Flushing should follow the configured task order (e.g., iterate buffers in insertion order and flush those marked finished).

Copilot uses AI. Check for mistakes.
@jdx jdx force-pushed the feat/keep-order-streaming branch from 7b9b083 to 35f0db4 Compare February 15, 2026 13:54
@github-actions
Copy link

github-actions bot commented Feb 15, 2026

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 x -- echo 24.3 ± 0.7 23.3 28.3 1.00
mise x -- echo 24.6 ± 0.9 23.4 30.7 1.01 ± 0.05

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 env 23.9 ± 0.8 22.7 28.3 1.00
mise env 23.9 ± 0.7 22.6 26.2 1.00 ± 0.05

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 hook-env 24.4 ± 0.7 23.4 26.9 1.00
mise hook-env 24.8 ± 0.8 23.6 27.2 1.01 ± 0.04

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 ls 22.7 ± 0.7 21.4 27.4 1.00
mise ls 23.0 ± 0.7 21.8 26.0 1.01 ± 0.04

xtasks/test/perf

Command mise-2026.2.13 mise Variance
install (cached) 128ms 128ms +0%
ls (cached) 80ms 80ms +0%
bin-paths (cached) 83ms 84ms -1%
task-ls (cached) 843ms 825ms +2%

@jdx jdx force-pushed the feat/keep-order-streaming branch from 35f0db4 to 1601835 Compare February 15, 2026 14:29
@jdx jdx force-pushed the feat/keep-order-streaming branch from 1601835 to af57b4f Compare February 15, 2026 14:51
if self.is_active(task) {
// Active task finished — clear it, flush waiting tasks, promote next
self.active = None;
self.buffers.shift_remove(task);
Copy link

Choose a reason for hiding this comment

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

Active task's buffered output discarded on finish

Low Severity

In on_task_finished, self.buffers.shift_remove(task) discards the removed buffer without printing it. This assumes the active task's buffer is empty, which is true for tasks initialized by init_task but not for tasks added to buffers dynamically via on_stdout/on_stderr's entry().or_default(). A task's first output line can get buffered (when is_active returns false because buffers.first() is None at that moment), yet a subsequent on_task_finished sees the task as active (since it's now first in buffers) and discards the buffer. Compare with promote_next, which correctly flushes via std::mem::take before streaming.

Additional Locations (1)

Fix in Cursor Fix in Web

jdx and others added 2 commits February 16, 2026 22:25
Previously, keep-order mode buffered ALL task output and printed
everything after all tasks completed. Now, one task at a time streams
its output live while other tasks buffer. When the active task finishes,
buffered output from already-finished tasks is flushed, then the next
running task is promoted to stream live.

Closes #8136

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Gate on_task_finished behind KeepOrder check to avoid unnecessary work
  in non-keep-order modes
- Update settings.toml description to reflect new streaming behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx jdx force-pushed the feat/keep-order-streaming branch from af57b4f to c2fe26c Compare February 16, 2026 22:28
@jdx
Copy link
Owner Author

jdx commented Feb 16, 2026

bugbot run

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

@jdx jdx enabled auto-merge (squash) February 16, 2026 22:48
@jdx jdx merged commit 4f33af1 into main Feb 16, 2026
33 checks passed
@jdx jdx deleted the feat/keep-order-streaming branch February 16, 2026 23:09
mise-en-dev added a commit that referenced this pull request Feb 17, 2026
### 🚀 Features

- **(task)** stream keep-order output in real-time per task by @jdx in
[#8164](#8164)

### 🐛 Bug Fixes

- **(aqua)** resolve lockfile artifacts for target platform (fix
discussion #7479) by @mackwic in
[#8183](#8183)
- **(exec)** strip shims from PATH to prevent recursive shim execution
by @jdx in [#8189](#8189)
- **(hook-env)** preserve PATH reordering done after activation by @jdx
in [#8190](#8190)
- **(lockfile)** resolve version aliases before lockfile lookup by @jdx
in [#8194](#8194)
- **(registry)** set helm-diff archive bin name to diff by @jean-humann
in [#8173](#8173)
- **(task)** improve source freshness checks with dynamic task dirs by
@rooperuu in [#8169](#8169)
- **(task)** resolve global tasks when running from monorepo root by
@jdx in [#8192](#8192)
- **(task)** prevent wildcard glob `test:*` from matching parent task
`test` by @jdx in [#8165](#8165)
- **(task)** resolve task_config.includes relative to config root by
@jdx in [#8193](#8193)
- **(upgrade)** skip untrusted tracked configs during upgrade by @jdx in
[#8195](#8195)

### 🚜 Refactor

- use enum for npm.pacakge_manager by @risu729 in
[#8180](#8180)

### 📚 Documentation

- **(plugins)** replace node/asdf-nodejs examples with vfox plugins by
@jdx in [#8191](#8191)

### ⚡ Performance

- call npm view only once by @risu729 in
[#8181](#8181)

### New Contributors

- @jean-humann made their first contribution in
[#8173](#8173)
- @mackwic made their first contribution in
[#8183](#8183)
- @rooperuu made their first contribution in
[#8169](#8169)

## 📦 Aqua Registry Updates

#### New Packages (2)

- [`BetterDiscord/cli`](https://github.com/BetterDiscord/cli)
- [`glossia.ai/cli`](https://github.com/glossia.ai/cli)
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
## Summary
- `keep-order` mode now streams the active task's output in real-time
instead of buffering everything until all tasks complete
- One task at a time is "active" and streams live; other tasks buffer.
When the active task finishes, finished tasks' buffers are flushed, then
the next running task is promoted to stream live
- Metadata messages (`$ command` echo, `Finished in` timing) are also
routed through the streaming state so they stay properly ordered with
task output

Closes jdx#8136

## Test plan
- [x] All 469 unit tests pass
- [x] `test_task_keep_order` e2e test passes
- [x] `test_task_sequence_keep_order` e2e test passes
- [x] All lint checks pass
- [ ] Manual testing with parallel tasks confirms streaming behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches core concurrent task output routing and ordering, so
regressions could manifest as missing/reordered output or deadlocks
under parallel runs.
> 
> **Overview**
> `keep-order` task output is reworked to **stream one “active” task in
real time** while buffering other tasks and flushing buffers in
*definition order* as tasks finish, instead of buffering everything
until the end.
> 
> This introduces a new `KeepOrderState` in `task_output_handler.rs`,
routes stdout/stderr and metadata (`eprint`) through it, and notifies it
on task completion from the scheduler; final result display now only
does a safety-net `flush_all`. Documentation for `task_output` is
updated, and the `test_task_keep_order` e2e test adds a parallel
slow/fast case to assert ordering is preserved even when completion
order differs.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8e22c89. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
### 🚀 Features

- **(task)** stream keep-order output in real-time per task by @jdx in
[jdx#8164](jdx#8164)

### 🐛 Bug Fixes

- **(aqua)** resolve lockfile artifacts for target platform (fix
discussion jdx#7479) by @mackwic in
[jdx#8183](jdx#8183)
- **(exec)** strip shims from PATH to prevent recursive shim execution
by @jdx in [jdx#8189](jdx#8189)
- **(hook-env)** preserve PATH reordering done after activation by @jdx
in [jdx#8190](jdx#8190)
- **(lockfile)** resolve version aliases before lockfile lookup by @jdx
in [jdx#8194](jdx#8194)
- **(registry)** set helm-diff archive bin name to diff by @jean-humann
in [jdx#8173](jdx#8173)
- **(task)** improve source freshness checks with dynamic task dirs by
@rooperuu in [jdx#8169](jdx#8169)
- **(task)** resolve global tasks when running from monorepo root by
@jdx in [jdx#8192](jdx#8192)
- **(task)** prevent wildcard glob `test:*` from matching parent task
`test` by @jdx in [jdx#8165](jdx#8165)
- **(task)** resolve task_config.includes relative to config root by
@jdx in [jdx#8193](jdx#8193)
- **(upgrade)** skip untrusted tracked configs during upgrade by @jdx in
[jdx#8195](jdx#8195)

### 🚜 Refactor

- use enum for npm.pacakge_manager by @risu729 in
[jdx#8180](jdx#8180)

### 📚 Documentation

- **(plugins)** replace node/asdf-nodejs examples with vfox plugins by
@jdx in [jdx#8191](jdx#8191)

### ⚡ Performance

- call npm view only once by @risu729 in
[jdx#8181](jdx#8181)

### New Contributors

- @jean-humann made their first contribution in
[jdx#8173](jdx#8173)
- @mackwic made their first contribution in
[jdx#8183](jdx#8183)
- @rooperuu made their first contribution in
[jdx#8169](jdx#8169)

## 📦 Aqua Registry Updates

#### New Packages (2)

- [`BetterDiscord/cli`](https://github.com/BetterDiscord/cli)
- [`glossia.ai/cli`](https://github.com/glossia.ai/cli)
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
### 🚀 Features

- **(task)** stream keep-order output in real-time per task by @jdx in
[jdx#8164](jdx#8164)

### 🐛 Bug Fixes

- **(aqua)** resolve lockfile artifacts for target platform (fix
discussion jdx#7479) by @mackwic in
[jdx#8183](jdx#8183)
- **(exec)** strip shims from PATH to prevent recursive shim execution
by @jdx in [jdx#8189](jdx#8189)
- **(hook-env)** preserve PATH reordering done after activation by @jdx
in [jdx#8190](jdx#8190)
- **(lockfile)** resolve version aliases before lockfile lookup by @jdx
in [jdx#8194](jdx#8194)
- **(registry)** set helm-diff archive bin name to diff by @jean-humann
in [jdx#8173](jdx#8173)
- **(task)** improve source freshness checks with dynamic task dirs by
@rooperuu in [jdx#8169](jdx#8169)
- **(task)** resolve global tasks when running from monorepo root by
@jdx in [jdx#8192](jdx#8192)
- **(task)** prevent wildcard glob `test:*` from matching parent task
`test` by @jdx in [jdx#8165](jdx#8165)
- **(task)** resolve task_config.includes relative to config root by
@jdx in [jdx#8193](jdx#8193)
- **(upgrade)** skip untrusted tracked configs during upgrade by @jdx in
[jdx#8195](jdx#8195)

### 🚜 Refactor

- use enum for npm.pacakge_manager by @risu729 in
[jdx#8180](jdx#8180)

### 📚 Documentation

- **(plugins)** replace node/asdf-nodejs examples with vfox plugins by
@jdx in [jdx#8191](jdx#8191)

### ⚡ Performance

- call npm view only once by @risu729 in
[jdx#8181](jdx#8181)

### New Contributors

- @jean-humann made their first contribution in
[jdx#8173](jdx#8173)
- @mackwic made their first contribution in
[jdx#8183](jdx#8183)
- @rooperuu made their first contribution in
[jdx#8169](jdx#8169)

## 📦 Aqua Registry Updates

#### New Packages (2)

- [`BetterDiscord/cli`](https://github.com/BetterDiscord/cli)
- [`glossia.ai/cli`](https://github.com/glossia.ai/cli)
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.

2 participants