Skip to content

🤖 tests: cover maybeStartQueuedTasksFromReservations paths before the split - #7744

Merged
ThomasK33 merged 4 commits into
mainfrom
refactor/task-queue-start-tests
Oct 30, 2026
Merged

ThomasK33 merged 4 commits into
mainfrom
refactor/task-queue-start-tests

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Summary

Adds 19 characterization tests for TaskService.maybeStartQueuedTasksFromReservations (the queue drain), with no production change. Line coverage of the method goes from 90 % (283 of 316 lines) to 100 %. This is the test PR at the start of stack 9 of the TaskService recovery and launch path split. The split PRs on top of it split the method (CC 76) into named steps.

What the tests cover

The new file is taskService.queuedDrain.test.ts. The tests start the drain through the public maybeStartQueuedTasks(). They check the plans that the drain hands to enqueueReservedTaskLaunch, the rows it marks failed, the rows it leaves queued, and the slots it uses. No test checks log or error text.

  • Slots: with one free slot, only the first queued task is reserved. A queued task that already streams becomes running and takes a slot.

  • Bad rows: a task without parentWorkspaceId, a parent without a workspace name, and a task without a workspace name are each marked failed. The next queued task still launches in the slot.

  • Fallbacks: when the parent metadata read fails, the plan carries fallback metadata, named by the folder or by the parent's project entry. When the skip_init_hook read throws, the launch goes on with skipInitHook false.

  • Workflows: a workflow-owned queued task whose run ends between the first owner check and the per-task loop is interrupted, not failed, and its slot goes to the next task.

  • Found by a mutation pass during the split (second commit): queued tasks launch oldest first; a task with an unlisted secondary project is marked failed; the metadata fallback uses the directory name when the parent's projects list names only other projects; once shutdown began, the drain stops at the first CAS, holds no lease for the next task and leaves both tasks queued; a reservation this process owns keeps its attempt id and abort signal; the handed-over row is starting with the plan's attempt id and the brief's send id.

  • Launch leases (third commit, from the stack's advisor): the shutdown exit, a lost CAS, and a throw out of the per-task loop each leave no launch lease held. Before this commit, deleting all three releases and the _unlaunchedLeases disposer failed no test. The tests read workspaceUseLeasesFor(config).heldCount(taskId, "launch").

Validation

  • A scratch edit of each covered branch failed its test. I restored the file after each edit.
  • The new file passed 100 runs (--rerun-each 10), and it passes with XUM_DISABLE_PROJECT_AUTOMATION set to 1 and to 0.
  • Setup installs tracking for every test. Teardown waits for every drain, launch and launch-failure handler, also when a test fails early.

Found while testing

A queued task that already streams uses two slots in one drain: countActiveAgentTasks counts it, and the drain adds one more. The test pins the current behaviour. #7743 tracks the fix.


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high • Cost: $307.02

… split

Adds characterization tests for the queue drain paths that no suite
reached, before that method is split into steps. The tests drive the
public maybeStartQueuedTasks and assert outcomes only: which plans the
drain handed to the launch, which rows it marked failed (interrupted
with a taskLaunchError), which it left queued, and slot use.

Paths covered: the slots-full break, a queued task that is already
streaming, a missing parentWorkspaceId, a parent without a workspace
name, a failed parent metadata read (fallback metadata, both
projectName sources), a failed skip_init_hook read (with a positive
control), a missing task workspace name, and a workflow-owned task whose
run ends between the prepass and the per-task loop.

- Line coverage of the method goes from 283/316 to 317/317.
- Each test failed under a scratch edit of the branch it covers.
- Setup installs call-through tracking of drains, launches, launch
  failure marks and background inits for every test, and teardown
  settles it before removing the root.

No production change.
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T06:42:39.239976Z 612939d Manual request
🔒 Security Review ✅ Completed 2026-10-30T06:43:08.127022Z 612939d Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 3fd6fc35cd

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 3fd6fc35cd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

🟢 e2e stories (Haiku): 14 passed

22 agent steps · 7 replayed from cache · 15 model calls · 46.3k tokens (28% cached)
Spend: $0.08 of the $1.00 cap (proxy ledger)

All 14 tests in 9 files
Test Agent Time
🟢 chatRestart.e2e.ts · 1 passed 1 step · 1 call 5.4s
🟢 a chat survives a restart of the server 1 step · 1 call 5.4s
🟢 compaction.e2e.ts · 1 passed 1 step · 1 call 4.2s
🟢 /compact replaces the old chat with a summary 1 step · 1 call 4.2s
🟢 pluginTrustRestart.e2e.ts · 1 passed 4 steps · 2 calls 9.8s
🟢 a trusted plugin hook stays trusted after the server restarts 4 steps · 2 calls 9.8s
🟢 settingsRoundTrip.e2e.ts · 1 passed 2 steps · 1 call 8.7s
🟢 a chat survives trips through Settings 2 steps · 1 call 8.7s
🟢 sidebarPerWorkspace.e2e.ts · 1 passed 4 steps · 1 call 4.2s
🟢 a new workspace starts with its right sidebar collapsed while another keeps it open 4 steps · 1 call 4.2s
🟢 smoke.e2e.ts · 1 passed 2 steps · 1 call 3.3s
🟢 the agent opens the playground workspace 2 steps · 1 call 3.3s
🟢 streamErrors.e2e.ts · 1 passed 1 step · 1 call 10.2s
🟢 stream errors show in the chat, and the chat recovers 1 step · 1 call 10.2s
🟢 todoNudge.e2e.ts · 1 passed 1 step · 1 call 3.0s
🟢 the agent finishes open TODO items after the turn-end nudge 1 step · 1 call 3.0s
🟢 touchAlignment.e2e.ts · 6 passed 6 steps · 6 calls 21.6s
🟢 the Settings close button is level with the title on a touch screen 390px wide 1 step · 1 call 4.1s
🟢 the Settings close button is level with the title on a touch screen 768px wide 1 step · 1 call 4.3s
🟢 Backup settings checkboxes are small and level with their labels on a touch phone 1 step · 1 call 4.6s
🟢 the heartbeat checkbox is small and level with its label on a touch phone 1 step · 1 call 3.0s
🟢 a reply's model and time stay on one line on a touch phone 1 step · 1 call 3.0s
🟢 the workspace actions menu stays on the title row on a touch phone 1 step · 1 call 2.6s

e2e 0.20.0 · 1m 14s · web · run artifacts

🟢 e2e Electron stories (Haiku): 3 passed

3 agent steps · 3 model calls · 7.9k tokens (33% cached)
Spend: $0.01 of the $1.00 cap (proxy ledger)

All 3 tests in 3 files
Test Agent Time
🟢 electron/deepLink.e2e.ts · 1 passed 1 step · 1 call 5.2s
🟢 a deep link of a second launch starts a chat in the running app 1 step · 1 call 5.2s
🟢 electron/menuBar.e2e.ts · 1 passed 1 step · 1 call 6.2s
🟢 the workspace menu bar is the desktop title bar 1 step · 1 call 6.2s
🟢 electron/smoke.e2e.ts · 1 passed 1 step · 1 call 5.1s
🟢 Xum Electron opens the playground workspace 1 step · 1 call 5.1s

e2e 0.20.0 · 18.8s · electron · run artifacts

🟢 e2e agent repros (Haiku): no tests selected

Spend: $0.00 of the $1.00 cap (proxy ledger)

e2e 0.20.0 · 5.3s · 2 targets (web, phone) · run artifacts

Adds queue drain tests for branches whose hand mutants survived the
queue-drain suites during the split of maybeStartQueuedTasksFromReservations.
They pass on the unsplit method and on the split. Each one fails under a
scratch edit of the branch it covers:

- Queued tasks are reserved oldest first when the config lists them
  newest first (the createdAt sort).
- A multi-project task with a secondary project the config does not list
  is marked failed, and the next task still launches.
- A failed parent metadata read whose projects list names only other
  projects falls back to the project directory's name.
- Once shutdown began, the drain stops at the first launch CAS: no lease
  hold for the next task, and both tasks stay queued.
- A reservation this process owns keeps its attempt id and its
  cancellation across the queue. A task without one gets a new id.
- When the drain hands the plan over, the row is `starting` with the
  plan's attempt id and the brief's send id.

No production change.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=xhigh -->
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 13aace4229

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 13aace4229

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

No test checked that the queue drain releases the launch lease of a task
it does not launch. A leaked lease makes removal of that task refuse until
the process exits.

- The shutdown test now checks that no launch lease stays held.
- New: a launch CAS lost to another writer releases the lease and plans
  nothing.
- New: a config write that throws out of the loop after an earlier task's
  plan leaves no lease held (the CAS-throw release and the
  `_unlaunchedLeases` disposer).
- The describe and header no longer say "before the split", and the
  streaming test names #7743 (the double slot).

Each new assertion fails when the release it covers is removed from
taskService.ts (scratch edit, reverted).
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 7f8e7c95d3

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f8e7c95d3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Addresses Codex review on #7744: the unowned case now asserts plan.abortSignal is undefined, so a plan that carries any other signal fails.
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 612939d50a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 612939d50a

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 66a22bc Oct 30, 2026
33 of 34 checks passed
@ThomasK33
ThomasK33 deleted the refactor/task-queue-start-tests branch October 30, 2026 07:41
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Oct 30, 2026
…ch CAS, bookkeeping and plans (coder#7745)

## Summary

Part 1 of 3 of the `maybeStartQueuedTasksFromReservations` split (stack
9, tail first). It moves the tail of the per-task body into steps: the
launch lease hold, the queued → starting CAS, the bookkeeping after it,
the plan build and the launch of the plans after the mutex is released.
The method's CC goes from 76 to 56. Changed lines: 317.

No behaviour change: same awaits, same calls to other methods in the
same order (checked with a script that inlines every step), same
`markTaskLaunchFailed` messages. Each new step has CC 8 or less, with
100 % line coverage. The method keeps the `_unlaunchedLeases` disposer,
the mutex block and the per-task loop. No step takes the mutex or starts
a drain, and every step call inside the mutex block is awaited.

## Implementation

- `leaseQueuedTaskLaunch` holds the launch lease before the CAS. On a
refusal it still calls `armQueuedLaunchLeaseRetry()` before it skips the
task.
- `claimQueuedTaskLaunch` runs the CAS. The updater keeps the
`isShuttingDown()` check, and `moveQueuedRowToStarting` picks the
attempt from the fresh row and writes `taskPromptSendId` in the same
updater call. The step reads `launch` and `shuttingDown` only after the
edit, as before, because `editConfig` can run the updater twice.
- Every exit without a plan releases the lease. The shutdown exit still
breaks the loop, and the plans collected before it still launch.
- `recordQueuedLaunch` pushes into the same `plans` array that the
disposer reads.

## Risks

Queued task launches. A wrong lease release would leave a task's lease
held until the process exits. The tests in coder#7744 cover the shutdown,
lost-CAS and lease-retry exits.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high` • Cost: `$313.07`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high
costs=313.07 -->
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Oct 30, 2026
…n preamble (coder#7747)

## Summary

Part 3 of 3. It moves the drain's preamble under the mutex into
`openQueuedDrain` (config, settings, queued list and the
inactive-workflow prepass) and `countQueuedDrainSlots` (the stale-turn
sweep bookkeeping and the capacity gates).
`maybeStartQueuedTasksFromReservations` ends at CC 8 (from 76 before the
stack). Changed lines: 172.

No behaviour change: same awaits, same calls to other methods in the
same order (checked with a script that inlines every step), same
`markTaskLaunchFailed` messages. Each new step has CC 8 or less, with
100 % line coverage. The method keeps the `_unlaunchedLeases` disposer,
the mutex block and the per-task loop. No step takes the mutex or starts
a drain, and every step call inside the mutex block is awaited.

## Implementation

- `countQueuedDrainSlots` reads the sweep generation before the turn
count and does the sweep bookkeeping only when the scan saw every owner.
- The turn count still runs after the prepass. A test in coder#7744 depends
on that order.

## Risks

Queue drains run after every task state change. The split adds
microtasks between tasks inside the mutex block (up to 7 before the next
task's first call), as the merged `recoverInterruptedTasks` split did.
All 5019 targeted tests pass at every part. A mutation pass over the new
code left 33 survivors: equivalent mutants, assertions for impossible
conditions, empty-string edge cases, one debug log, and two test gaps
(an extra coalesced drain, and a worktree parent's runtime project
path).

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high` • Cost: `$313.07`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high
costs=313.07 -->
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.

1 participant