feat(dashboard): show delayed node eligibility time - #862
Open
breken-ai wants to merge 1 commit into
Open
Conversation
A node delayed by ReQueueAfterSignal/RequeueAtSignal, a retry-policy backoff or a graph start_delay stores the instant it becomes eligible in State.enqueue_after (epoch ms, picked up when enqueue_after <= now), but NodeRunDetailsResponse never carried it, so the Node Details modal showed a CREATED node with no explanation of the delay. Expose the stored enqueue_after as an optional field on the node run details response (passed through, not recomputed) and render it in the modal only for CREATED states: "Eligible after" while the instant is in the future, "Eligible since" once it has passed and the node is still waiting for a worker. The copy states that this is an eligibility time, not a guaranteed start time. Older records/clients without the field see nothing (no Invalid Date, no epoch zero); already queued or finished nodes carry no waiting message. The wire representation stays epoch milliseconds; formatting to the viewer's locale/timezone happens in a pure helper covered by fake-clock tests (node --test, no new dependencies). The modal pairs each clock reading with the details it was taken for and clears its timers when the details change or it unmounts. Scheduler dispatch logic is untouched. Closes FailproofAI#611 Claude-Session: https://claude.ai/code/session_01DkE5qM85ht9Uoq3aAqcKf7
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.
Closes #611
What goes wrong
When a node is delayed (
ReQueueAfterSignal/RequeueAtSignal, a retry-policy backoff, or a graphstart_delay) the state manager stores the moment it becomes eligible inState.enqueue_after(epoch milliseconds;enqueue_statespicks states withenqueue_after <= now). That value never reaches the dashboard:NodeRunDetailsResponsedoes not carry it, so the Node Details modal shows aCREATEDnode with no hint of why it is not running or when it may.What changes
state-manager
NodeRunDetailsResponse.enqueue_after: Optional[int] = None(epoch ms), described as the stored eligibility time and explicitly not a guaranteed execution time.get_node_run_detailspassesstate.enqueue_afterthrough verbatim (stored value, not recomputed).dashboard
NodeRunDetailsResponse.enqueue_after?: number | nullin the client types.src/lib/nodeEligibility.ts:getNodeEligibility(details, nowMs)returnsscheduled(future) orwaiting(past-due, still not picked up) only forCREATEDstates with a usable timestamp;nullotherwise.formatEligibilityTimerenders the instant in the viewer's locale/timezone with a zone label. Wire representation stays epoch ms; nothing localized is ever sent back.NodeDetailsModalshows, under Status & Timestamps:<local time TZ>— "Delayed by a start delay, retry policy or requeue signal. A worker can pick this node up any time after this moment; it is not a guaranteed start time."<local time TZ>— "Eligible to run and waiting for a worker to pick it up."The clock reading is paired with the details object it was taken for, so a node opened long after page load is never classified against a stale reading. A 5 s interval flips scheduled → waiting on time; the timeout and interval are cleared whenever details change or the modal unmounts.
npm testscript (node --test, zero new dependencies) for the helper;allowImportingTsExtensionsintsconfig.json(valid because the project isnoEmit) so the test can import./nodeEligibility.tsthe way Node's type-stripping loader requires.docs/docs/exosphere/dashboard.mdand a feature bullet indashboard/README.md.What deliberately does not change
enqueue_states, the requeue signals and the retry path are untouched. This is observability only.enqueue_afteralready exists on everyStatedocument.QUEUED/EXECUTED/SUCCESS/ERRORED/… states, so a node that was already picked up never carries a stale "waiting" message.undefined/null/0) show nothing — noInvalid Date, no 1970 epoch.MagicMockfixtures still pass because the field is optional.Tests
tests/unit/controller/test_get_node_run_details.py+2 (exposes_stored_enqueue_after,response_enqueue_after_is_optional). Full suite 639 → 641 passed.src/lib/nodeEligibility.test.ts0 → 7 passed (fake clock via injectednowMs, no real timers): future delayed node, past-due node, boundaryat == now(matches the server's<=), missing/null/0/NaNfield, every non-CREATEDstatus,null/undefineddetails, and the same instant rendered in UTC / Asia/Tokyo / America/Los_Angeles across a day boundary.Both new tests fail on
main(AttributeError: 'NodeRunDetailsResponse' object has no attribute 'enqueue_after';ERR_MODULE_NOT_FOUNDfor the helper).Evidence record
https://claude.ai/code/session_01DkE5qM85ht9Uoq3aAqcKf7