Skip to content

A routine's Run now says what it will spend, before it spends it (fix #1506) - #1636

Merged
suleimansh merged 2 commits into
mainfrom
routine-work-tooltips
Aug 22, 2026
Merged

A routine's Run now says what it will spend, before it spends it (fix #1506)#1636
suleimansh merged 2 commits into
mainfrom
routine-work-tooltips

Conversation

@suleimansh

Copy link
Copy Markdown
Contributor

Closes #1506.

The problem in one line: Run now on the Routine work card spends an agent on a model and a place that are nowhere near the button. #1634 gave you a way to go and set them; this says what they currently are, before you press anything.

What it looks like

Hovering any Run now:

Bring tickets/ up to date with the GitHub issues. An empty tickets/ gets a full first import.
Claude Code · Opus · This machine
Starts one agent in gemstack, unattended — nothing is asked mid-run.

Three lines for the issue's three questions — what happens, which model, where it runs — plus the one fact that is arguably the biggest hidden cost: a card-fired routine runs unattended (#1279), so nothing will be asked mid-run.

Two decisions worth your eye

The first line is the preset's own sentence, not new copy. Presets already carry a one-liner that the launcher's menu shows on hover; the routine now carries it too, exactly the way it already carries the preset's label. So the launcher and the card describe the same work in the same words, and rewording a preset rewords both.

Two presets had no such sentence and now do — Plan tickets ("Turn tickets/*.md into costed plans (tickets/*.plan.md)") and Drain queue ("Work the entries already on the queue (TODO_AGENTS.md)"). Plan tickets is in the launcher menu, so that one is user-visible there too. Reword either if you want them said differently.

The drain row reports something else entirely, and it has to. Its Run now is the sweep, not a start, and two things the other rows promise are simply untrue of it:

  • it visits every project the daemon watches, not the one picked in "Run in";
  • each of those resolves its own committed the-framework.yml on top of these preferences (resolveProjectAgentOptions, daemon-services.ts:149), so naming a model here would be a guess.

So it says:

Work the entries already on the queue (TODO_AGENTS.md)
Each project's own settings decide the model and where it runs.
Sweeps every project the daemon watches, up to 2 agents each, unattended.

The number is the live concurrency setting from the same card.

How it's built

The model lists, the run-target names and the "nothing pinned" wording moved into dashboard/lib/agent-settings.ts. They were spread across four surfaces — the launcher's driver tree, the gear's "Run on" list, the Settings page and now this card — and a tooltip that reports settings is only worth anything if it says exactly what the launcher says. One side effect: the Settings page called the local machine "This device" while the gear called it "This machine"; there is one name now.

A model is still only named inside its own driver's list (#1143): nothing pinned, or a model pinned on the other driver, reads as "the CLI's own default" rather than borrowing the first entry.

Verification

Dogfooded on the real daemon, both rows and both refactored surfaces:

  • Overview → hover Run now on "Update from GitHub" → the three lines above, with Claude Code · Opus · This machine matching the actual preferences
  • hover the drain row's Run now → the sweep wording instead, with the live concurrency of 2
  • the launcher's model menu still lists Fable / Opus / Sonnet / Haiku with Opus checked, i.e. the moved catalog still feeds the tree it came from

Tests: 4 new, each proven by breaking the code it guards. Dropping the preset's sentence, falling back to the first model when none is pinned, hardcoding the run target to local, giving the drain row the ordinary settings line, and ignoring the concurrency in its fan-out line each failed exactly the intended test and nothing else. Full suite green: 1536 node + 792 dashboard.

RoutineWork.SPEC.md, its test SPEC, preset-catalog.SPEC.md, the new agent-settings.SPEC.md and FEATURES-SPEC.md all updated.

Hovering a Run now on the Routine work card now names the three things the
card could not otherwise show: what that routine does, which model its start
would use, and where it runs. All three come from the Global options a page
away, so the button's cost was invisible until the agent already existed.

The first line is the preset's own sentence rather than one written again for
this card, carried onto the job like its label already is, so the launcher and
the routines list describe the same work with the same words. Two presets had
no such sentence, and now do.

The drain row answers differently, because its Run now is the sweep: it visits
every project the daemon watches, and each of those resolves its own committed
settings on top of these, so the model and place the other rows promise would
both be a guess there.

The model lists, the run-target names and the "nothing pinned" wording move to
`dashboard/lib/agent-settings.ts`: four surfaces name them now, and a tooltip
that reports settings has to say exactly what the launcher says.
The launcher's preset hints rendered with the right words and were covered by
the very menu that triggered them, so the one-liner a preset carries could not
be read from the place it is offered.

`z-50` sat on the popup, which Base UI leaves `position: static` — where a
z-index does nothing at all. The menu and the popover both carry theirs on
their own positioner, so both won. It moves to the positioner here too.

Found by hovering the two preset one-liners this branch adds; it is not new,
and it was true of every tooltip opened over a menu.
@suleimansh

Copy link
Copy Markdown
Contributor Author

🤖 automated

A second commit: the launcher's preset hints were being painted behind the menu that triggered them. Not a regression from this branch — I found it by hovering the two preset one-liners the branch adds, and it was equally true of the ones that shipped months ago.

components/ui/tooltip.tsx put z-50 on the popup, and Base UI leaves that element position: static, where a z-index does nothing at all. The menu and the popover both carry theirs on their positioner, which is absolutely positioned — so both won, every time.

Measured in the page before changing anything, with the "Suggest new features" hint open inside the presets menu:

tooltip rect x 264→546, y 454→480
menu rect x 277→533, y 168→858
elementFromPoint at the tooltip's own centre SPAN of the menu item

So the hint was rendering correctly and losing the paint. Setting the z-index on the positioner in the live page flipped that same probe to the tooltip itself, which is exactly what the one-line change does.

Fixed, rebuilt, and re-checked in the browser: Turn tickets/.md into costed plans (tickets/.plan.md) now reads over the menu. Full suite still green (1536 node + 792 dashboard).

No test. jsdom computes no stacking and the classes carry no CSS there, so the only assertable thing would be the class name — which would pass whether or not the tooltip is visible to a human, and would have passed for the whole time this was broken. The reason it must sit on the positioner is written where the change is instead.

@suleimansh
suleimansh merged commit d62a280 into main Aug 22, 2026
2 checks passed
@suleimansh
suleimansh deleted the routine-work-tooltips branch August 22, 2026 14:22
suleimansh added a commit that referenced this pull request Aug 22, 2026
…1506) (#1636)

* A routine's Run now says what it will spend, before it spends it (#1506)

Hovering a Run now on the Routine work card now names the three things the
card could not otherwise show: what that routine does, which model its start
would use, and where it runs. All three come from the Global options a page
away, so the button's cost was invisible until the agent already existed.

The first line is the preset's own sentence rather than one written again for
this card, carried onto the job like its label already is, so the launcher and
the routines list describe the same work with the same words. Two presets had
no such sentence, and now do.

The drain row answers differently, because its Run now is the sweep: it visits
every project the daemon watches, and each of those resolves its own committed
settings on top of these, so the model and place the other rows promise would
both be a guess there.

The model lists, the run-target names and the "nothing pinned" wording move to
`dashboard/lib/agent-settings.ts`: four surfaces name them now, and a tooltip
that reports settings has to say exactly what the launcher says.

* A tooltip is drawn above what it opens over (#1506)

The launcher's preset hints rendered with the right words and were covered by
the very menu that triggered them, so the one-liner a preset carries could not
be read from the place it is offered.

`z-50` sat on the popup, which Base UI leaves `position: static` — where a
z-index does nothing at all. The menu and the popover both carry theirs on
their own positioner, so both won. It moves to the positioner here too.

Found by hovering the two preset one-liners this branch adds; it is not new,
and it was true of every tooltip opened over a menu.
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.

Add tooltips to prompt buttons

1 participant