Skip to content

fix(dispatcher): _getState() reports "stopped" for a genuine external stop (aicshud/WHEEL#1028) - #143

Merged
so5 merged 2 commits into
RIKEN-RCCS:mainfrom
so5:fix/issue-1028-getState-stopped
Sep 16, 2026
Merged

so5 merged 2 commits into
RIKEN-RCCS:mainfrom
so5:fix/issue-1028-getState-stopped

Conversation

@so5

@so5 so5 commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes aicshud/WHEEL#1028: _getState() never returned "stopped", the documented state for an externally-stopped project (documentMD/user_guide/_reference/3_workflow_screen/1_graphview.md), even though start()'s onStop handler sets stoppedExternally (aicshud/WHEEL#1020) specifically to record that case.

Currently masked at the whole-project level because onStopProject() unconditionally force-overwrites the project state to "stopped" afterward - but runProject() also writes _getState()'s (wrong) return value straight to the root workflow component's own cmp.wheel.json, so that component's own recorded state stayed wrong ("finished"/"failed"/"unknown") even though the project overall correctly showed "stopped".

What changed

  • _getState() now falls through to "stopped" once hasUnknownComponent/hasFailedComponent are ruled out, instead of never reporting the documented "stopped" state at all. Checked last, not first: stopProject() is also used to abort the rest of a project when one of its own tasks fails (the "taskStateChanged" listener in handlers/projectController.js), and that path already records the failure via setStateFlag() before remove() runs - hasFailedComponent/hasUnknownComponent must keep taking priority over stoppedExternally there, matching the existing "failed must not be clobbered by stopped" contract (issue #1000).
  • Moved where stoppedExternally is set from the generic "stop" handler (onStop, inside start()) to remove() itself, the one and only caller of which is stopProject(). pause() is also called directly by _jumpHandler's "break" handling as a normal, successful in-workflow loop exit - not a stop - and both emit the same "stop" event that onStop listens for, so leaving the flag in the generic handler would have made a break-terminated loop's own state incorrectly report "stopped" too.

Testing

  • Test-first: red test committed separately, then the fix. Also updated the existing #1020 test to call remove() (what stopProject() actually calls) instead of a bare pause(), and added a new test confirming a bare pause() (the "break" path) does NOT set stoppedExternally.
  • npm run testDocker -w server — full suite green (1669 passing, 15 pending, 0 failing).

Related

Filed against aicshud/WHEEL#1028 on GitLab (issue tracker for this project). Builds on aicshud/WHEEL#1020 (#140, merged), #1023 (#141, merged), #1027 (#142, merged).

🤖 Generated with Claude Code

https://claude.ai/code/session_01C3jKNM1qubomM8UTRdkEWu

so5 and others added 2 commits September 16, 2026 21:07
_getState() never returns "stopped", the documented state for an
externally-stopped project (documentMD/user_guide/_reference/
3_workflow_screen/1_graphview.md), even though start()'s onStop handler
sets stoppedExternally (aicshud/WHEEL#1020) specifically to record that
case. Currently masked at the whole-project level because
onStopProject() unconditionally force-overwrites the project state to
"stopped" afterward - but runProject() also writes _getState()'s (wrong)
return value straight to the root workflow component's own
cmp.wheel.json, so that component's own recorded state stays wrong
("finished"/"failed"/"unknown") even though the project overall
correctly shows "stopped".

Also update the existing #1020 test to call remove() (what stopProject()
actually calls) instead of a bare pause() - the two are not equivalent:
pause() alone is also used internally by _jumpHandler's "break" handling
(a normal, successful in-workflow loop exit, not a stop), and the
existing #_getState reproduction below documents that a break-terminated
loop's own state must still be "finished", not "stopped".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C3jKNM1qubomM8UTRdkEWu
… stop (aicshud/WHEEL#1028)

- _getState() now falls through to "stopped" (aicshud/WHEEL#1020's
  stoppedExternally flag) once hasUnknownComponent/hasFailedComponent
  are ruled out, instead of never reporting the documented "stopped"
  state at all. Checked last, not first: stopProject() is also used to
  abort the rest of a project when one of its own tasks fails (the
  "taskStateChanged" listener in handlers/projectController.js), and
  that path already records the failure via setStateFlag() before
  remove() runs - hasFailedComponent/hasUnknownComponent must keep
  taking priority over stoppedExternally there, matching the existing
  "failed must not be clobbered by stopped" contract (issue #1000).

- Move where stoppedExternally is set from the generic "stop" handler
  (onStop, inside start()) to remove() itself, the one and only caller
  of which is stopProject(). pause() is also called directly by
  _jumpHandler's "break" handling as a normal, successful in-workflow
  loop exit - not a stop - and both emit the same "stop" event that
  onStop listens for, so leaving the flag in the generic handler would
  have made a break-terminated loop's own state incorrectly report
  "stopped" too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C3jKNM1qubomM8UTRdkEWu
@so5
so5 merged commit 112998d into RIKEN-RCCS:main Sep 16, 2026
@so5
so5 deleted the fix/issue-1028-getState-stopped branch September 16, 2026 12:24
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