feat(flowchat): add a thread goal control to the composer workspace strip - #3252
Merged
Merged
Conversation
…sion Editing a paused goal restarts it, so the edit path delivered objective-updated steering and awaited it. On an idle session that delivery admits the goal's next turn, which measured 2482-3844 ms and made the edit dialog look frozen. Hand the steering over in the background, the way resume already does, through one shared delivery helper for both kinds. The goal state, its event, and the release of the interruption that parked the kickoff stay on the command path, so an edited goal still comes back as active.
Give the running goal a control next to the local workspace chip: goal icon, status, and a live elapsed timer, plus pause and clear quick actions. Pause acts immediately instead of opening a confirmation, while clear keeps its own dialog. Render goal duration in days, hours, minutes and seconds instead of raw seconds, and bring an edited goal back to running so the pause it was edited from no longer describes it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add a thread goal control next to the local workspace chip in the composer strip, and fix the goal actions that did not match what the user asked for.
The strip now shows the goal icon, its status and a live elapsed timer, with pause, resume and clear available directly from it. Pause and clear stop the goal-driven turn instead of letting it keep running, pause acts immediately without a confirmation dialog, and clearing a goal no longer leaves the next
/goalkickoff parked. Goal duration renders in days, hours, minutes and seconds instead of raw seconds, and editing a paused goal returns it to running because the edit itself is the user asking the goal to continue.Editing a goal also no longer blocks the dialog. The command used to wait for the goal's next turn to be admitted, which measured 2482.3 ms and 3843.6 ms on the reporting user's machine, and it now hands that steering to the runtime in the background the way resume already does.
Type and Areas
Type: Feature, bug fix, UI/UX.
Areas: Rust core (agentic coordination: thread goal lifecycle and session scheduler), web UI (flow chat composer strip, thread goal control and dialogs), i18n locales (en-US, zh-CN, zh-TW).
Motivation / Impact
A running goal was only visible inside the goal dialog, so the user could not tell what the agent was working toward, how long it had been running, or stop it without opening that dialog. The new control puts the goal icon, its status and a ticking elapsed timer beside the local workspace chip, and exposes pause, resume and clear as direct actions.
Three behavior bugs are fixed in the same area. Pause and clear previously marked the goal but let the goal-driven turn run to completion, so the goal kept working after the user stopped it. Pause opened a confirmation dialog although the user had already decided. After clearing a goal, sending
/goalagain could leave the goal active with a parked kickoff, which looked like the goal running while the flow chat stayed empty and the timer only moved when the user clicked something.Goal duration is now reported in days, hours, minutes and seconds in the strip, the goal detail dialog and the usage report, instead of a raw seconds count such as "91s". Editing a paused goal leaves it running, which is the intended reading of the edit dialog: the user is telling the goal what to work on next, so the pause it was edited from no longer describes it.
The edit command no longer waits for the goal's next turn to be admitted. The goal state, its update event and the release of the interruption that parked the kickoff stay on the command path, while the objective steering is delivered in the background through the same helper the resume path uses. The measured edit command went from 2482.3 ms and 3843.6 ms to roughly 50 ms, so the dialog closes immediately and the goal turn appears a moment later in the flow chat.
No persisted shape changes. Thread goal records, statuses and the goal store schema are untouched, so existing goals and older builds keep working.
Verification
Rust:
cargo check -p openbitfun-core --no-default-features --features agent-runtime,git— passed.cargo test -p openbitfun-core --no-default-features --features agent-runtime,git --lib goal— 25 passed, 1 failed. The failure is the pre-existingagentic::coordination::scheduler::tests::thread_goal_plain_prompt_steering_activates_without_an_extra_turn, which asserts a\ninside a template that is checked out as CRLF on Windows (src/crates/execution/agent-runtime/src/thread_goal/templates/objective_updated.mdcontains 16 CRLF pairs). It is a line-ending assertion in the test, not a behavior change from these commits.pnpm run fmt:rs— applied to the changed Rust files.Web UI:
pnpm --dir src/web-ui run test:run src/flow_chat/utils/threadGoalDuration.test.ts src/flow_chat/components/usage/usageReportUtils.test.ts src/flow_chat/components/thread-goal/threadGoalStripTone.test.ts src/flow_chat/components/ChatInputWorkspaceStrip.test.tsx— 4 files, 73 tests passed.pnpm run type-check:web— passed.pnpm run i18n:audit— passed with 0 warnings.pnpm run appearance:contract-audit— passed.node scripts/audit-frontend-colors.mjs --surface web-ui— passed for 2436 files.pnpm run typography:audit— passed with zero violations.Timing evidence comes from the desktop client log of the reporting user, where the frontend records each invoke duration:
update_session_thread_goal_objectivetook 2482.3 ms and 3843.6 ms, whileset_session_thread_goal_statustook 86 ms to 428 ms. In the same log the backend shows 2071 ms and 3213 ms inside the goal turn admission, betweenDialog turn workspace contextandSaved dialog turn, with no other backend event in that window, which is what the edit command was waiting on.Reviewer Notes
Manual check worth running before merge: open a paused goal, edit the objective, and confirm the dialog closes immediately, the strip switches to running with the timer advancing, and the next goal turn appears in the flow chat within a few seconds.
The backend change is deliberately asymmetric.
/goal <objective>still awaits its delivery, because that command's success already implies the kickoff turn exists, while edit and resume hand delivery to the runtime in the background. Making goal creation return instantly as well is a small follow-up onset_thread_goal_objective. Note that a background delivery reports failure through awarn!log only, which is the existing behavior of the resume path.Rollback is a plain revert of these two commits; nothing is persisted in a new shape and no migration is involved.
Checklist
Generated with OpenBitFun