fix(dashboard): handle User action requests from the needs-you drawer - #5571
loopx-agent wants to merge 2 commits into
Conversation
… drawer A user_action attention item only offered a note and an 'Explain this decision' button whose callback was never wired. The drawer now offers 'I've done this' (todo.update complete), 'Reply in conversation' (drafts an editable message in the Goal chat), and Other options: defer until tomorrow 09:00 (todo.update defer with resume_at), 'No longer needed' (gate.resolve cancel, the outcome the User completion owner admits for a user_action), and 'Ask the Agent to explain'. Preview failures surface inline and can be retried; read-only sources and rows without a stable todo_id explain why the action is unavailable. Attention owner actions move from context-drawer.tsx into attention-actions.tsx. Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Semantic smoke for user_action classification, stable-id and lifecycle fencing and the defer preset; a browser scenario for done with preview failure/recovery and readback, defer, no-longer-needed, reply drafting and the missing-id state; and real ChatActionService coverage of the exact drawer payloads across legacy, file and sqlite authorities. Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
|
This pull request has merge conflicts with Choose the remote for the base repository, not an out-of-date fork. git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEADFor a same-repository clone whose Keep the DCO |
Delivery brief
user_actionitem in the personal workspace's needs you drawer could not be handled.task_class=user_action), the drawer only showed "This request asks for a reply or an action, not an approval…" and an Explain this decision button. That button was dead:onExplainDecisionwas declared onPersonalWorkspaceCallbacksbut no caller ever provided it. The owner could not mark the item done, reply, defer it, or close it, and the heading called it a decision.todo.update/gate.resolveChat actions and the TS User completion owner (loopx/control_plane/todos/user_completion.ts). This adds no new action kind and no Python decision logic.ChatActionServicetests that send the drawer's exact payloads to legacy, file and sqlite authorities.Journey (before → after)
关于「…」:). Nothing is sent automatically.todo.update defer,resume_when=resume_at:<tomorrow 09:00 local, tz-aware>)gate.resolve decision=cancel, which the User completion owner admits for auser_action; it closes only the reminder and never resumes or approves other work)todo_idUser gates (
user_gate) still show approve / reject / withdraw unchanged; only their preview errors are now surfaced.Changes
attention-actions.tsx(new): all owner actions for one attention item (decision, User action, fallback note), with pending/error state. These actions moved out ofcontext-drawer.tsx, which keeps shrinking (bounded future-facing refactor: one module now owns attention write affordances).attention-details.ts:interactionnow mirrors the typedtask_class(decision | user_action | unknown). Prose never classifies an item. AddstodoIdentified,canHandleUserAction, andnextMorningResumeWhen.personal-workspace-page.tsx: providesonReplyToAttentionand a defaultonExplainDecisionthat open the item's Goal chat with a draft. The draft waits until that Goal's composer is visible.i18n.tsx: matching English and Simplified Chinese copy.Vocabulary:
AttentionDetails.interactionextends a local display union that mirrors the registered Todotask_classvocabulary (loopx/control_plane/todos/summary_lanes.ts,contract.py). It creates no new shared contract.generate_semantic_inventory.py --changed-from HEADreports no new carriers. The probe covers onlyloopx/, so the dashboard union was reviewed by hand.Behavior change disclosure: a
user_actionrow'sinteractionwasunknownand is nowuser_action. The existing smoke assertion "prose never classifies interaction" was updated to keep its real invariant: only the typed class classifies, and approval wording never grants approve/reject.Entrypoints
todo.update/gate.resolveowners already serve them.Validation (exact head)
npx tsc --noEmit: passnpm run smoke:attention-details(TZ Asia/Shanghai, America/New_York, Europe/London): passpersonal-workspace-contract.test.mjs: passpytest tests/control_plane/test_chat_user_action_handling.py tests/control_plane/test_chat_gate_decisions.py tests/presentation/test_chat_bundle.py: 38 passedscripts/chat_bundle.py build --install, then:dashboard-attention-details-browser-smoke.mjs(packaged, zh/en/mobile/read-only): passpersonal-workspace-browser-smoke.mjspackaged, all 42 scenarios, including the newattention-user-action: passloopx canary premerge --from-git-diff: passed. One inherited advisory:control-plane-maintainability-ratchet-smoke.py, a known baseline failure that does not mention the changed files.The new browser scenario uses a synthetic fixture and never touches real Goals. It checks:
todo.update completeon the righttodo_id, and nothing is written before confirmation;resume_at, and no-longer-needed sendsgate.resolve cancel;Visual evidence: before/after screenshots of the drawer (initial state, other options, preview failure, confirm, board readback, missing-id state, reply draft) were captured from the packaged build and shared with the owner. They are not committed.
Remaining gaps
todo.update deferon a User Todo at preview ("requires the current active lease execution proof"). The drawer shows that error and writes nothing. Done and no-longer-needed work under hard lease (covered by tests). Whether User-Todo deferral should need a lease is a control-plane owner question and is out of scope here.user_actionreturns to needs-you automatically when itsresume_atarrives. Meanwhile it stays visible as deferred in the Tasks view.notewould replace the request's original reason. Replies go through the conversation draft instead.Future-facing refactor pass: applied. Attention write affordances moved into
attention-actions.tsx, and decision preview errors now share the same surfaced state.Not self-merged: this changes
apps/**product behavior and is left for the maintainer.